> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scaleflow.fr/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook leads

> Créer un prospect dans votre pipeline de vente depuis n'importe quel outil.

```text theme={null}
POST https://api.scaleflow.fr/webhooks/ingest-lead?endpoint_id=VOTRE_ENDPOINT_ID
```

Chaque appel crée un prospect dans votre espace, ou retrouve celui qui existe déjà. Aucun lead n'est créé en double.

## Exemple

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.scaleflow.fr/webhooks/ingest-lead?endpoint_id=VOTRE_ENDPOINT_ID" \
    -H "Authorization: Bearer VOTRE_JETON" \
    -H "Content-Type: application/json" \
    -d '{
      "first_name": "Jean",
      "last_name": "Dupont",
      "email": "jean.dupont@example.com",
      "phone": "+33612345678",
      "source": "meta-ads",
      "tag_ids": ["8b1f2c3d-0000-4000-8000-000000000000"],
      "pipeline": "Setting",
      "stage": "À contacter"
    }'
  ```

  ```javascript Node.js theme={null}
  const reponse = await fetch(
    "https://api.scaleflow.fr/webhooks/ingest-lead?endpoint_id=VOTRE_ENDPOINT_ID",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SCALEFLOW_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        first_name: "Jean",
        email: "jean.dupont@example.com",
        source: "landing-page",
      }),
    },
  );
  const resultat = await reponse.json();
  ```

  ```python Python theme={null}
  import os, requests

  resultat = requests.post(
      "https://api.scaleflow.fr/webhooks/ingest-lead",
      params={"endpoint_id": "VOTRE_ENDPOINT_ID"},
      headers={"Authorization": f"Bearer {os.environ['SCALEFLOW_TOKEN']}"},
      json={"first_name": "Jean", "email": "jean.dupont@example.com"},
  ).json()
  ```
</CodeGroup>

## Champs du corps

Tous les champs sont facultatifs, mais **au moins un** de `email`, `phone` ou `instagram_url` est obligatoire.

<ParamField body="first_name" type="string">
  Prénom.
</ParamField>

<ParamField body="last_name" type="string">
  Nom.
</ParamField>

<ParamField body="email" type="string">
  Adresse e-mail. Sert à reconnaître un contact déjà connu, sans tenir compte des majuscules.
</ParamField>

<ParamField body="phone" type="string">
  Téléphone. Il sert aussi à reconnaître un contact, mais à l'identique : envoyez toujours le même format, de préférence international (`+33612345678`).
</ParamField>

<ParamField body="instagram_url" type="string">
  Lien du profil Instagram.
</ParamField>

<ParamField body="source" type="string">
  Origine du lead (`meta-ads`, `landing-page`, `salon`…). Texte libre, affiché sur la fiche.
</ParamField>

<ParamField body="tag_ids" type="string[]">
  Tags à poser sur le prospect, par **identifiant** (UUID), pas par nom. Les identifiants sont dans la colonne « tag\_id » du tableau des tags, dans **Réglages → Tags & Webhook**. Un identifiant inconnu est ignoré et signalé dans `warnings`.
</ParamField>

<ParamField body="pipeline" type="string">
  Pipeline d'arrivée : nom exact ou identifiant. Sans ce champ, le lead va dans le pipeline par défaut.
</ParamField>

<ParamField body="stage" type="string">
  Étape d'arrivée dans ce pipeline : nom exact ou identifiant. Sans ce champ, le lead va à la première étape.
</ParamField>

<Note>
  Les autres champs sont **ignorés**, et chacun est signalé dans `warnings`. Un champ `tags` (noms de tags) est aussi ignoré : utilisez `tag_ids`.
</Note>

## Contact déjà connu

ScaleFlow cherche d'abord un prospect avec le même e-mail, puis avec le même téléphone. S'il le trouve :

* aucune nouvelle fiche n'est créée, la réponse vaut `"status": "existing"` ;
* le prénom, le nom, l'e-mail et le téléphone sont remplacés par les valeurs envoyées quand elles ne sont pas vides ; la `source` d'origine, elle, est conservée ;
* le prospect **n'est pas déplacé** dans le pipeline, même si vous envoyez `pipeline` et `stage`, pour ne pas écraser sa position dans le funnel.

Vous pouvez donc renvoyer le même lead sans risque de doublon.

## Réponse

<ResponseExample>
  ```json 200 theme={null}
  {
    "status": "created",
    "prospect_id": "5d0c6a2e-0000-4000-8000-000000000000",
    "pipeline_id": "a1b2c3d4-0000-4000-8000-000000000000",
    "stage_id": "e5f6a7b8-0000-4000-8000-000000000000",
    "placement": "demande",
    "tags_applied": [
      { "id": "8b1f2c3d-0000-4000-8000-000000000000", "name": "Conf Club" }
    ],
    "warnings": []
  }
  ```
</ResponseExample>

<ResponseField name="status" type="string">
  `created` : nouveau prospect. `existing` : contact déjà connu, fiche mise à jour.
</ResponseField>

<ResponseField name="prospect_id" type="string">
  Identifiant du prospect.
</ResponseField>

<ResponseField name="pipeline_id" type="string | null">
  Pipeline où se trouve réellement le prospect.
</ResponseField>

<ResponseField name="stage_id" type="string | null">
  Étape où se trouve réellement le prospect.
</ResponseField>

<ResponseField name="placement" type="string">
  Comment le placement a été décidé :

  * `demande` : le pipeline et l'étape demandés ont été appliqués ;
  * `defaut` : pipeline par défaut et première étape ;
  * `existant` : contact déjà connu, laissé où il était ;
  * `aucun` : l'espace n'a aucun pipeline, le prospect est créé mais n'apparaît sur aucun tableau.
</ResponseField>

<ResponseField name="tags_applied" type="object[]">
  Tags effectivement posés, avec leur nom.
</ResponseField>

<ResponseField name="warnings" type="string[]">
  Avertissements en français. L'envoi a réussi, mais quelque chose n'a pas été appliqué comme demandé : champ inconnu, tag introuvable, pipeline ou étape introuvable (le lead part alors au défaut), contact existant non déplacé.
</ResponseField>

<Tip>
  Un nom de pipeline ou d'étape mal orthographié ne fait **pas** échouer l'appel : le lead est créé au défaut et l'erreur apparaît dans `warnings`. Surveillez ce champ pendant vos tests.
</Tip>

## Erreurs

| Code | `error` | Cause |
| - | - | - |
| 400 | `invalid_endpoint_id` | `endpoint_id` n'est pas un UUID |
| 400 | `invalid_payload` | corps qui n'est pas un objet JSON, ou ni `email`, ni `phone`, ni `instagram_url` |
| 401 | `unauthorized` | jeton absent, invalide, ou webhook désactivé |
| 404 | `not_found` | `endpoint_id` absent de l'URL |
| 500 | `ingest_failed` | erreur interne, réessayez plus tard |

Les limites communes (débit, taille) sont dans [Erreurs et limites](/erreurs-et-limites).

## Avec Make, Zapier ou n8n

Utilisez le module HTTP générique de l'outil (**HTTP → Make a request** dans Make, **Webhooks by Zapier → POST** dans Zapier, nœud **HTTP Request** dans n8n) :

1. **Méthode** : `POST`.
2. **URL** : l'URL copiée depuis ScaleFlow, avec son `endpoint_id`.
3. **En-tête** : `Authorization` = `Bearer VOTRE_JETON`.
4. **Corps** : JSON, en associant les champs de votre formulaire à `first_name`, `email`, `phone`, etc.

Un champ de formulaire laissé vide peut être envoyé tel quel, il est traité comme absent.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.