Leads
1 endpoint
/v1/leads/captureCapturer un lead depuis un funnel externe
Capture un nouveau lead dans Capturia depuis un funnel, formulaire ou outil tiers (Make, Zapier, n8n, landing custom). Une fois capturé, le lead apparaît immédiatement dans Mes Prospects côté dashboard et le bot SMS prend le relais dans les 30 secondes si le consentement et la configuration sont en ordre.
Contact déjà connu (courriel ou téléphone)
La fiche est résolue par courriel d'abord (insensible à la casse),
puis par téléphone, en incluant les coordonnées secondaires déjà
rattachées à une fiche. Capturer un lead dont le courriel ou le
téléphone existe déjà réutilise et enrichit la fiche existante — le
contact_id retourné est celui de cette fiche, jamais un doublon et
jamais une erreur. Les coordonnées et champs cœur déjà remplis (nom,
courriel, téléphone, pipeline, valeur, adresse...) ne sont pas
écrasés ; les réponses de qualification (custom_fields) sont en
revanche rafraîchies avec les dernières valeurs envoyées — une clé
déjà présente est remplacée, une clé nouvelle s'ajoute. Un nouveau
téléphone est conservé comme numéro secondaire quand la fiche en a
déjà un. Si le courriel et le téléphone pointent deux fiches
différentes, le courriel a priorité et aucune coordonnée n'est retirée
à l'autre fiche.
La réponse expose contact_was_new pour distinguer les deux issues :
sur une fiche existante (false), pipeline et assignment_status
décrivent la demande résolue, pas forcément l'état persisté — la fiche
conserve son pipeline, son étape et son vendeur déjà en place.
Contact déjà en pipeline ouvert
Un lead dont le contact (résolu par courriel ou téléphone) est déjà dans
une étape de pipeline ouverte (déjà pris en charge par un vendeur — non
archivé, étape ni gagnée ni perdue)
n'est pas re-soumis : la réponse renvoie status: existing_pipeline_contact,
aucune nouvelle soumission n'est enregistrée et le bot SMS n'est pas
redéclenché, pour ne pas écraser une conversation en cours. Les champs
cœur nouvellement fournis enrichissent quand même le contact existant.
La dédup de retry reste prioritaire : un re-push reçu dans la fenêtre de
5 minutes est traité comme un retry (status: idempotent_replay, voir
Idempotency) et conserve l'activity_id et l'original_created_at
d'origine — existing_pipeline_contact ne s'applique qu'au-delà de cette
fenêtre.
Idempotency
Le header Idempotency-Key est optionnel mais recommandé pour les retries
côté client. Une 2e requête avec la même clé et le même payload renvoie la
réponse d'origine sans dupliquer le lead. La clé est stockée 24h. Une 2e
requête avec la même clé mais un payload différent retourne 409
idempotency_conflict. La RPC sous-jacente conserve par ailleurs sa propre
fenêtre de dedup à 5 minutes. Cette fenêtre est par contact et
indépendante du contenu : toute soumission d'un contact déjà capturé
dans les 5 dernières minutes est traitée comme un retry
(idempotent_replay) même si le payload diffère — la fiche est tout de
même enrichie (dont les custom_fields, rafraîchis), mais aucune
nouvelle activité, aucun tag, et pas de relance du bot SMS. Les mises à
jour de la fiche restent réelles : une automatisation qui surveille un
champ personnalisé réagit au changement de valeur, comme pour toute
modification de la fiche. C'est aussi ce qui protège d'une double
relance du bot quand un lead re-soumet son formulaire coup sur coup.
Un Idempotency-Key différent ne contourne pas cette fenêtre : elle
s'applique en base après tout cache miss. Deux soumissions
volontairement distinctes du même contact ne produisent deux activités
que si elles sont espacées de plus de 5 minutes.
Codes d'erreur custom
Pour des raisons historiques, certains rejets retournent des codes hors catalog standard :
- 422 —
invalid_phone,missing_consent,invalid_email,invalid_payload,pipeline_not_found,stage_not_found(au lieu devalidation_error/business_rule_violation). Body shape simplifiée propre à cette route — voir la réponse 422 ci-dessous. - 429 —
rate_limited_ipquand la limite IP (100 req/min) est atteinte (au lieu derate_limit_exceeded). Le headerX-RateLimit-Scope: ipidentifie le scope de la limite déclenchée. Body shape simplifiée (pas derequest_idnidetailsstandard) — à harmoniser dans une phase ultérieure.
leads:captureCorps de requête
#/components/schemas/LeadCapturePayload
Responses
| Status | Description |
|---|---|
200 | Lead capturé ( |
400 | La requête est mal formée (header manquant, JSON invalide, paramètre invalide). |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
409 | Une requête précédente avec la même |
422 | Payload refusé par la validation ( |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
Exemple de requête
curl -X POST 'https://app.capturia.io/api/v1/leads/capture' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"phone": "+15145551234",
"sms_consent": true,
"first_name": "Alex",
"last_name": "Tremblay",
"email": "[email protected]",
"source_label": "funnel-fb-jan",
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "spring-2026"
},
"tags": [
"lead-chaud",
"vu-webinaire"
],
"pipeline": {
"pipeline_slug": "ventes-b2b",
"stage_slug": "nouveau-lead",
"deal_value": 2500,
"expected_close_date": "2026-06-30",
"assigned_sales_rep_email": "[email protected]"
},
"custom_fields": {
"type_propriete": "maison_isolee",
"delai_vente": "0_3_mois"
},
"address": "123 rue Principale, Montréal",
"postal_code": "H2X 1Y4"
}'