Réception de leads multi-contrat depuis le site web
Ce document décrit un contrat API unique par lequel le backend du site envoie les données de lead, les coordonnées du visiteur et les métriques UTM marketing à ReactiveCRM en une seule requête.
1. Objectif
L'intégrateur du site n'a pas besoin de créer séparément le contact, le téléphone, l'email, le lead et l'événement marketing. Une seule requête doit créer atomiquement :
- le contact ;
- le téléphone principal du contact ;
- l'email principal du contact, s'il est fourni ;
- le lead ;
- le lien entre le contact et le lead ;
- l'événement marketing et l'attribution UTM.
Si une étape échoue, l'ensemble de la requête est annulé et les données partiellement créées ne sont pas enregistrées.
2. Endpoint
POST /api/site/leads
Authorization: Bearer <CRM_ACCESS_TOKEN>
Content-Type: application/json
Le token doit être transmis uniquement depuis le backend du site ou une fonction serverless. Ne placez jamais le token CRM dans le JavaScript du navigateur.
Le locataire est déterminé à partir du token d'autorisation. Tous les UUID fournis sont validés dans ce locataire.
3. Contrat JSON complet
{
"externalId": "site-form-01J7K9A2M4Y5T6",
"lead": {
"leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47",
"quality": "WARM",
"message": "Je souhaite une consultation sur la mise en œuvre du CRM"
},
"contact": {
"firstName": "Ivan",
"lastName": "Ivanov",
"phone": "+79991234567",
"email": "ivan@example.com"
},
"marketing": {
"occurredAt": "2026-09-06T18:00:00Z",
"visitorId": "visitor-8b7f",
"sessionId": "session-31ac",
"source": "website",
"channel": "paid",
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "summer-consulting",
"utmContent": "banner-a",
"utmTerm": "crm consultation",
"gclid": "EAIaIQobChMI-example",
"fbclid": null,
"landingUrl": "https://example.com/consultation",
"referrer": "https://www.google.com/"
}
}
4. Champs de la requête
4.1. Champs racine
| Champ | Type | Requis | Description |
|---|---|---|---|
externalId | chaîne | recommandé | ID unique stable de l'envoi du formulaire pour l'idempotence |
lead | objet | oui | Données du lead en cours de création |
contact | objet | oui | Données de la personne de contact |
marketing | objet | non | Métriques UTM et données techniques de la visite marketing |
externalId est généré avant la première tentative d'envoi. Lors de la répétition de la requête après un délai d'attente ou une erreur réseau, utilisez la même valeur.
4.2. L'objet lead
| Champ | Type | Requis | Description |
|---|---|---|---|
leadSourceId | uuid | oui | ID de la source de leads dans le locataire actuel |
quality | chaîne | non | Évaluation initiale : HOT, WARM, COOL ou COLD |
message | chaîne | non | Message du visiteur ou commentaire sur l'envoi |
Si le site n'effectue pas de pré-qualification, il est préférable de ne pas envoyer le champ quality.
4.3. L'objet contact
| Champ | Type | Requis | Description |
|---|---|---|---|
firstName | chaîne | oui | Prénom de la personne de contact |
lastName | chaîne | non | Nom de famille de la personne de contact |
phone | chaîne | oui | Téléphone principal ; format E.164 recommandé |
email | chaîne(email) | non | Email principal de la personne de contact |
Les données minimales requises pour traiter un envoi sont firstName et phone.
4.4. L'objet marketing
| Champ | Type | Requis | Description |
|---|---|---|---|
occurredAt | date-heure | non | Heure de l'envoi du formulaire ; l'heure CRM est utilisée en l'absence |
visitorId | chaîne | non | ID de visiteur anonyme |
sessionId | chaîne | non | ID de session du site |
source | chaîne | non | Source de l'événement, par défaut website |
channel | chaîne | non | Canal : ex. paid, organic, social, email, direct |
utmSource | chaîne | non | Valeur de utm_source |
utmMedium | chaîne | non | Valeur de utm_medium |
utmCampaign | chaîne | non | Valeur de utm_campaign |
utmContent | chaîne | non | Valeur de utm_content |
utmTerm | chaîne | non | Valeur de utm_term |
gclid | chaîne | non | ID de clic Google |
fbclid | chaîne | non | ID de clic Meta/Facebook |
landingUrl | chaîne | non | URL de la page de destination |
referrer | chaîne | non | URL de la page précédente |
Les données personnelles ne doivent pas être dupliquées dans l'objet marketing. Le nom, le téléphone, l'email et le texte du message sont transmis uniquement dans contact et lead.
5. Requête minimale
{
"externalId": "site-form-550e8400-e29b-41d4-a716-446655440000",
"lead": {
"leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47"
},
"contact": {
"firstName": "Ivan",
"phone": "+79991234567"
}
}
6. Réponse réussie
Première requête — 201 Created
{
"leadId": "22222222-2222-2222-2222-222222222222",
"contactId": "33333333-3333-3333-3333-333333333333",
"phoneId": "44444444-4444-4444-4444-444444444444",
"emailId": "55555555-5555-5555-5555-555555555555",
"marketingEventId": "66666666-6666-6666-6666-666666666666",
"duplicate": false,
"createdAt": "2026-09-06T18:00:01Z"
}
Si les données d'email ou de marketing ne sont pas fournies, les ID correspondants sont renvoyés sous forme de null.
Répétition avec le même externalId — 200 OK
{
"leadId": "22222222-2222-2222-2222-222222222222",
"contactId": "33333333-3333-3333-3333-333333333333",
"phoneId": "44444444-4444-4444-4444-444444444444",
"emailId": "55555555-5555-5555-5555-555555555555",
"marketingEventId": "66666666-6666-6666-6666-666666666666",
"duplicate": true,
"createdAt": "2026-09-06T18:00:01Z"
}
Une requête répétée ne doit pas créer un second lead ou contact.
7. Exemple cURL
curl -X POST "$CRM_BASE_URL/api/site/leads" \
-H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalId": "site-form-01J7K9A2M4Y5T6",
"lead": {
"leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47",
"quality": "WARM",
"message": "Rappelez-moi"
},
"contact": {
"firstName": "Ivan",
"lastName": "Ivanov",
"phone": "+79991234567",
"email": "ivan@example.com"
},
"marketing": {
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "crm-demo",
"landingUrl": "https://example.com/demo"
}
}'
8. Interfaces TypeScript
type LeadQuality = 'HOT' | 'WARM' | 'COOL' | 'COLD';
interface SiteLeadRequest {
externalId?: string;
lead: {
leadSourceId: string;
quality?: LeadQuality;
message?: string;
};
contact: {
firstName: string;
lastName?: string;
phone: string;
email?: string;
};
marketing?: {
occurredAt?: string;
visitorId?: string;
sessionId?: string;
source?: string;
channel?: string;
utmSource?: string;
utmMedium?: string;
utmCampaign?: string;
utmContent?: string;
utmTerm?: string;
gclid?: string;
fbclid?: string;
landingUrl?: string;
referrer?: string;
};
}
interface SiteLeadResponse {
leadId: string;
contactId: string;
phoneId: string;
emailId: string | null;
marketingEventId: string | null;
duplicate: boolean;
createdAt: string;
}
9. Exemple côté serveur
export async function sendLeadToCrm(payload: SiteLeadRequest): Promise<SiteLeadResponse> {
const response = await fetch(`${process.env.CRM_BASE_URL}/api/site/leads`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRM_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const body = await response.text();
throw new Error(`ReactiveCRM returned ${response.status}: ${body}`);
}
return response.json() as Promise<SiteLeadResponse>;
}
10. Erreurs
| HTTP | Cause | Action de l'intégrateur |
|---|---|---|
400 | Erreur de format, champ requis manquant, email/téléphone/quality invalide | Corrigez les données ; ne réessayez pas automatiquement sans modifications |
401 | Token manquant ou invalide | Rafraîchissez le token d'intégration |
403 | Pas d'accès au locataire ou à l'opération | Vérifiez l'utilisateur d'intégration et le rôle |
404 | leadSourceId non trouvé dans le locataire actuel | Mettez à jour l'ID de la source dans les paramètres du site |
409 | externalId est déjà lié à une requête incompatible | Vérifiez la génération de l'ID et le journal d'intégration |
5xx | Erreur CRM transitoire | Réessayez la même requête avec le même externalId |
Exemple d'erreur de validation :
{
"status": 400,
"error": "Bad Request",
"message": "contact.phone must not be blank",
"path": "/api/site/leads"
}
11. Idempotence
externalIddoit être unique dans le locataire et la sourcewebsite.- Générez
externalIdavant la première requête et stockez-le dans l'envoi du site. - En cas de délai d'attente, réessayez la requête avec le même corps et le même
externalId. - Ne créez pas de nouveau
externalIdlors d'une nouvelle tentative d'un même envoi. - Si
duplicate: true, l'envoi a déjà été traité et est considéré comme livré avec succès.
12. Mini-guide pour le développeur du site
- Lors de la première visite, stockez les balises UTM,
gclid,fbclid, l'URL de destination et le référent. - Lorsque le formulaire est soumis, générez un
externalIdstable. - Envoyez le formulaire au backend du site.
- Le backend ajoute le token CRM et appelle
POST /api/site/leads. - Sur
201ou200avecduplicate: true, considérez l'envoi comme livré. - En cas de délai d'attente ou de
5xx, réessayez la requête avec le mêmeexternalId. - N'envoyez jamais le token CRM directement depuis le navigateur.
13. Ce que le gestionnaire CRM obtient
Après une requête réussie, le CRM contient un lead qui :
- a la source
leadSourceIddéfinie ; - conserve le message du visiteur ;
- a l'évaluation de qualité initiale, si le site l'a fournie ;
- est lié à un contact avec un nom et un téléphone principal ;
- est lié à un email principal, s'il est fourni ;
- conserve les métriques UTM et les identifiants publicitaires pour l'analyse.
Cet ensemble est suffisant pour que le gestionnaire voie l'envoi, appelle le contact et qualifie le lead.
Reactive CRM