API et intégrations
LinkedFlow dialogue avec le reste de vos outils par trois chemins : une API REST avec jeton, un webhook qui déclenche des workflows depuis l'extérieur, et des nœuds qui appellent vos propres services depuis l'intérieur d'un parcours. Cette page est le contrat exact, avec des exemples prêts pour n8n, Make ou un simple curl.
L'API utilisée par l'application est l'API publique : mêmes endpoints, même authentification. Pour une intégration, n'utilisez pas le jeton du navigateur : créez un jeton d'API dans Paramètres → Jetons d'API, avec l'expiration de votre choix et rattaché à cet espace.
Les trois chemins
| Chemin | Qui appelle qui | À quoi ça sert |
|---|---|---|
| API REST | Votre outil → LinkedFlow | Créer des prospects et des listes, lancer des workflows, lire les exécutions, résoudre des approbations, récupérer le tunnel. |
| Webhook entrant | Votre outil → un workflow précis | Démarrer un parcours pour un prospect qui existe déjà dans le CRM, le jeton du workflow servant d'identifiant. |
| Nœuds sortants | LinkedFlow → votre outil | Prévenir votre CRM ou n8n en plein parcours (Webhook sortant) ou lui demander une valeur et continuer avec la réponse (Requête HTTP). |
Authentification
Toute l'API vit sous https://app.linkedflow.pro/api/v1 et s'authentifie par un jeton dans l'en-tête Authorization. Il y a deux façons d'en obtenir un, et seule la première est faite pour les intégrations.
Jeton d'API (celui qu'il vous faut pour n8n)
Dans l'application : Paramètres → Jetons d'API → Nouveau jeton. Donnez-lui un nom (« n8n · signaux quotidiens »), choisissez l'expiration — 30 jours, 90 jours, 1 an ou sans expiration — et copiez-le : il ne s'affiche qu'une fois.
| Propriété | Ce que ça veut dire |
|---|---|
| Rattaché à un espace | Celui où il a été créé, sans possibilité d'en changer : même si vous changez d'espace dans l'application ou que quelqu'un envoie l'en-tête X-Workspace, ce jeton continue d'écrire là où il a été configuré. |
| Hérite de votre rôle | Il n'ajoute aucun droit. Passez de MANAGER à VIEWER et ses appels en écriture renvoient 403. |
| Survit à votre mot de passe | Changer de mot de passe ferme les autres sessions du navigateur mais ne révoque pas les jetons d'API : une rotation ne casse jamais vos intégrations. |
| Révocable à l'instant | Depuis le même écran. Il cesse de fonctionner à l'appel suivant, et cet écran indique aussi sa dernière utilisation. |
Conseil : créez un utilisateur dédié à la machine (« automatisation@votreentreprise.com ») avec le rôle minimal nécessaire et générez le jeton depuis ce compte. L'activité de l'espace distingue alors ce qu'a fait un humain de ce qu'a fait n8n, et vous pouvez lui couper l'accès sans toucher au vôtre.
Jeton de session (pour un script jetable)
Le login de l'application renvoie aussi un jeton, mais il expire au bout de 30 jours et suit l'espace actif de votre utilisateur. Parfait pour un curl de test ; pas pour une automatisation censée durer des mois :
curl -X POST https://app.linkedflow.pro/api/v1/auth/login \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email":"vous@email.com","password":"votre-mot-de-passe"}'
{
"user": { "id": "…", "name": "Valen", "current_workspace": { "id": "…", "role": "OWNER" } },
"token": "198|eMdBPK8g…"
}
Ensuite, chaque appel porte deux en-têtes :
Authorization: Bearer 198|eMdBPK8g…
Accept: application/json
Le jeton est votre mot de passe. Stockez-le comme identifiant dans n8n (Header Auth : nom Authorization, valeur Bearer <jeton>), jamais dans une URL ni dans le corps d'un nœud. Si un jeton d'API fuite, révoquez-le dans Paramètres → Jetons d'API : c'est immédiat et sans effet sur les autres.
Espace de travail et rôles
Inutile d'envoyer l'espace de travail : le jeton agit toujours sur l'espace courant de l'utilisateur. Si vous appartenez à plusieurs, POST /auth/switch-workspace/{workspace} en change. Chaque endpoint exige le même rôle que l'écran équivalent dans l'application (VIEWER < MEMBER < MANAGER < ADMIN < OWNER) ; sinon, c'est un 403. Les rôles sont détaillés dans Paramètres.
Conseil : créez un utilisateur dédié à la machine (« automatisation@votreentreprise.com ») avec le rôle minimum nécessaire. Vous pourrez lui couper l'accès sans toucher au vôtre, et l'activité de l'espace distinguera ce qu'a fait un humain de ce qu'a fait n8n.
Conventions
- Sans
Accept: application/json, pas de JSON : l'application répondrait par une redirection ou du HTML. C'est l'erreur numéro un des débuts. - Erreurs :
401jeton absent ou expiré,403rôle insuffisant ou abonnement inactif,404hors de votre espace,422validation ({"message":"…","errors":{"champ":["…"]}}),429trop d'appels. - Collections : les prospects arrivent paginés (
data,current_page,last_page,total) ; d'autres ressources renvoient la collection brute ou l'enveloppent dansitems. Regardez toujours la première réponse avant de mapper les champs. - Langue : les messages d'erreur sortent dans la langue de votre utilisateur. On peut la forcer avec
Accept-Language: fr. - Rythme : pas de limite globale, mais les endpoints coûteux ont la leur (le webhook de workflow, 30 appels par minute ; le compositeur IA, 20).
Endpoints principaux
La liste complète est celle de l'application ; voici ce qui sert vraiment depuis l'extérieur.
Prospects et listes
| Endpoint | Rôle | Ce qu'il fait |
|---|---|---|
GET /prospects | VIEWER | Liste paginée. Filtres : search, status, list_id, tag. |
POST /prospects | MEMBER | Crée un prospect. Accepte list_id pour le déposer dans une liste au passage. |
GET /prospects/{id} | VIEWER | Fiche complète. |
PUT /prospects/{id} | MEMBER | Modifie des champs (pas le statut : c'est le moteur qui le fait avancer). |
POST /prospects/bulk-tags | MEMBER | Étiquette en masse. |
GET /prospects/{id}/conversation | VIEWER | Le fil réel de LinkedIn. |
POST /prospects/{id}/conversation/messages | MANAGER | Envoie un vrai message (quotas et horaires s'appliquent). |
GET · POST /lists | VIEWER · MEMBER | Lister et créer des listes. |
POST /lists/{id}/prospects | MEMBER | Ajoute des prospects existants à une liste. |
POST /suppression | MANAGER | Envoie quelqu'un dans la liste de suppression. |
Workflows et exécutions
| Endpoint | Rôle | Ce qu'il fait |
|---|---|---|
GET /workflows | VIEWER | Liste avec statut, version courante et dernière exécution. |
POST /workflows/{id}/run | MANAGER | Lance sur list_id ou prospect_ids[]. Accepte account_id et allow_repeat. |
POST /workflows/{id}/activate · /pause | MANAGER | Allume ou éteint les déclencheurs. |
POST /workflows/{id}/validate · /dry-run | MEMBER | Valide et simule sans toucher LinkedIn. |
GET /executions · /executions/{id} | VIEWER | Exécutions et leur détail avec KPI par nœud. |
GET /prospect-executions/{id} | VIEWER | Le parcours d'une personne, nœud par nœud. |
POST /executions/{id}/cancel | MANAGER | Arrête ce qui reste en attente. |
GET /approvals · POST /approvals/{id}/approve | VIEWER · MANAGER | La boîte d'approbations depuis l'extérieur — depuis Slack, par exemple. |
GET /dashboard/funnel · /stats · /activity | VIEWER | Les chiffres du tableau de bord, pour vos propres rapports. |
GET /errors/unmanaged · /paused | VIEWER | Ce qui attend une décision humaine. Parfait pour une alerte quotidienne. |
La recette qui règle 80 % des cas : créer et déclencher
Un nouveau prospect entre dans le CRM et, si vous le déposez dans une liste, le workflow qui écoute cette liste démarre tout seul. Deux appels, voire un seul :
curl -X POST https://app.linkedflow.pro/api/v1/prospects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{
"linkedin_url": "https://www.linkedin.com/in/quelquun",
"first_name": "Ana",
"company": "Conseil Nord",
"tags": ["n8n", "signal:recrute"],
"source": "n8n",
"list_id": "01a05d4e-23b2-725c-9aa4-e1ed9e8fd6da"
}'
D'où vient le list_id ? De l'application : Prospects → cliquez sur la liste pour la sélectionner → bouton Copier l'ID. Par l'API, GET /lists renvoie l'id de chacune. C'est un UUID (01a05d4e-23b2-725c-…) ; envoyez autre chose et vous obtenez un 422 indiquant que list_id n'est pas valide.
Trois choses à savoir sur cet appel :
linkedin_urloupublic_identifiersuffit. Si l'URL n'est pas un profil, c'est un422.- Un doublon répond
409et ce n'est pas un échec : il renvoie le prospect qui existait déjà et l'ajoute quand même à la liste. Dans n8n, configurez le nœud pour ne pas s'arrêter sur 409 — c'est le chemin normal quand vous retraitez une source. - L'entrée dans la liste déclenche le trigger « Ajouté à une liste » de tout workflow actif qui l'écoute, que le prospect soit tout neuf ou qu'il existait déjà sans être dedans. C'est l'accroche : n8n n'a rien à savoir des workflows, il lui suffit de remplir une liste.
Un list_id bien formé mais inexistant (ou appartenant à un autre espace) ne renvoie pas d'erreur : le prospect est créé et n'entre dans aucune liste. C'est de l'isolation entre locataires — la réponse ne doit pas révéler les listes des autres — alors vérifiez le champ lists de la réponse si vous voulez être sûr.
Si vous préférez déclencher explicitement, utilisez POST /workflows/{id}/run avec {"list_id":"…"} ou {"prospect_ids":["…"]}. Il faut le rôle MANAGER et une version enregistrée du workflow.
Webhook entrant : déclencher un workflow depuis l'extérieur
Un workflow qui commence par le nœud Webhook expose une URL avec son propre jeton (visible dans le builder, rôle MANAGER ou plus) :
POST https://app.linkedflow.pro/api/v1/webhooks/workflows/{token}
Content-Type: application/json
{ "linkedin_url": "https://www.linkedin.com/in/quelquun" }
Identifiants valides, au moins un : public_identifier, linkedin_url ou email. Tout ce que vous envoyez en plus reste disponible dans le parcours sous {{trigger.*}} : vous pouvez donc transmettre le motif du signal et l'utiliser dans le texte du message.
| Réponse | Signification |
|---|---|
202 {"status":"accepted"} | Reçu. C'est toujours celle-ci, que le prospect existe ou non et que le parcours démarre ou non. |
404 | Jeton inconnu, workflow inactif, ou premier nœud qui n'est pas un Webhook. |
422 | Aucun identifiant envoyé. |
Cet endpoint ne crée pas de prospects. Il résout quelqu'un qui est déjà dans votre CRM ; s'il ne trouve personne, il répond quand même 202 et rien ne se passe. C'est délibéré : la réponse ne doit pas permettre de deviner qui figure ou non dans votre base. Si votre intégration amène de nouvelles personnes, créez-les d'abord avec POST /prospects.
Un 202 ne garantit pas non plus le démarrage : un prospect supprimé, marqué « ne pas contacter » ou qui a déjà répondu reste dehors, à cause des protections habituelles. Ce qui s'est réellement passé se voit dans Exécutions.
Sorties : quand LinkedFlow appelle votre outil
Webhook sortant
Le nœud Webhook sortant fait un POST vers l'URL que vous indiquez. Sans corps personnalisé, il envoie ceci :
{
"prospect": {
"id": "01a0…",
"full_name": "Ana García",
"linkedin_url": "https://www.linkedin.com/in/…",
"company": "Conseil Nord",
"status": "REPLIED"
},
"execution_id": "01a0…",
"node_id": "webhook_1"
}
Le POST n'est pas signé, et le nœud n'a que deux champs : méthode et URL. Ni en-têtes ni corps personnalisable — vous ne pouvez donc pas envoyer de signature ni de secret dans un en-tête. Tout ce qui doit voyager avec passe par l'URL, qui accepte les variables :
https://votre-n8n/webhook/reponses?s=UN-SECRET-LONG&cat={{previous.classification}}
Dans n8n cela arrive comme {{ $json.query.s }} et {{ $json.query.cat }} — pas dans headers ni dans body. Vérifiez le secret avec un nœud Si juste après le webhook et jetez ce qui ne l'a pas : sans ce contrôle, quiconque trouve l'URL peut vous injecter de fausses données.
Détails opérationnels : 15 secondes de délai maximum ; un 5xx est considéré comme passager et retenté jusqu'à trois fois avec attente croissante ; un 4xx est marqué comme erreur de configuration et attend votre décision dans la boîte d'erreurs. Répondez vite et en 200 : si votre automatisation est lente, acceptez d'abord et traitez ensuite.
Requête HTTP : demander une valeur et continuer
Le nœud Requête HTTP est identique, mais pensé pour exploiter la réponse. Ce que vous renvoyez est disponible dans le nœud suivant :
{{previous.status}} → 200
{{previous.response.tier}} → "A" (votre JSON, sous "response")
{{previous.response.angle}} → "recrute des commerciaux"
Avec ça, un nœud Si bifurque sur votre propre scoring : {{previous.response.tier}} == "A". Renvoyez toujours un JSON plat et répondez en moins de 15 secondes ; si votre service peut être lent, renvoyez une valeur par défaut plutôt que de faire attendre le parcours.
Cinq flux n8n qui fonctionnent
1 · Signal externe → prise de contact (capter)
Schedule à 7 h 30 → HTTP Request vers votre source de signaux (publications, changements de poste, offres d'emploi) → Code qui normalise en linkedin_url → Remove Duplicates → HTTP Request vers POST /prospects avec le list_id de « Signaux du jour » et le signal dans tags.
Dans LinkedFlow : un workflow actif déclenché par Ajouté à une liste → Lire le profil → Score IA → Si {{previous.score}} >= 70 → Réagir à sa dernière publication → Attendre 1 jour → Envoyer l'invitation avec note. Configurez le nœud n8n pour traiter le 409 comme un succès.
2 · Réponse classée → CRM et agenda (traiter)
Dans LinkedFlow : déclencheur Message reçu → nœud Classer la réponse → toutes les sorties vers le même Webhook sortant, avec la catégorie dans l'URL : ?cat={{previous.classification}}.
Dans n8n : Webhook → un Si comparant {{ $json.query.s }} à votre secret → Switch sur {{ $json.query.cat }} : INTERESTED crée l'affaire dans votre CRM, prévient Slack et envoie le lien de prise de rendez-vous ; QUESTION rédige un brouillon avec l'IA et le dépose dans Slack pour approbation ; NOT_NOW écrit la date de relance dans une feuille et un cron la réinjecte à 90 jours ; DO_NOT_CONTACT appelle POST /suppression et referme.
3 · « Commentez GUIDE et je vous l'envoie »
Dans LinkedFlow : déclencheur Commentaire sur une publication avec mot-clé → Réagir au commentaire → Répondre au commentaire → Envoyer le message avec le lien → Webhook sortant.
Dans n8n : inscription dans votre outil d'emailing, ligne dans une feuille avec commenté → livré → ouvert, et au bout de trois jours, s'il n'y a pas eu de réponse, un POST vers le webhook entrant d'un workflow de relance. Rappel : les commentaires sont sondés toutes les 5 minutes et répondre à un commentaire ne consomme pas le quota de messages.
4 · Enrichissement et scoring maison en plein parcours
Ici, c'est LinkedFlow qui appelle n8n. Dans le parcours : Lire le profil → Requête HTTP vers votre webhook n8n (réponse par le dernier nœud) → Si {{previous.response.tier}} == "A" → invitation avec note sur mesure ; sinon, direction une liste de nurturing.
Dans n8n : Webhook → scraping du site de l'entreprise → un modèle d'IA qui renvoie un JSON strict (tier, angle, douleur) → Respond to Webhook. Prévoyez un repli (tier: "B") pour qu'une panne chez vous ne bloque jamais le parcours.
5 · Le rapport du lundi (surveiller)
Schedule lundi 7 h → GET /dashboard/funnel et GET /executions → GET /errors/unmanaged → un modèle d'IA avec la consigne « trois décisions, pas trois graphiques » → email. Aucun nouveau tableau de bord : les chiffres viennent de l'API qui dessine déjà celui de l'application.
Téléchargez-les tout faits
Quatre workflows n8n prêts à importer depuis un fichier. Avant de les activer : créez un identifiant Header Auth nommé Authorization avec la valeur Bearer <votre-jeton>, et remplacez l'id de liste d'exemple par le vôtre.
- 1 · Signal externe → prospect dans une liste — celui qui règle 80 % des cas.
- 2 · Réponse classée → CRM et agenda — avec la vérification de l'en-tête secret.
- 3 · Enrichissement et scoring — celui qui répond au nœud Requête HTTP.
- 4 · Le rapport du lundi — tunnel, exécutions et erreurs via l'API.
Bonnes pratiques
- Dédupliquez avant d'appeler, par
linkedin_urlet par jour. Deux POST identiques ne dupliquent pas le prospect, mais peuvent le faire passer deux fois dans le flux si vous le retirez de la liste puis l'y remettez. - Ne luttez pas contre les quotas. Peu importe la vitesse d'envoi : les actions sortent au rythme du compte (25 visites, 20 invitations et 30 messages par jour par défaut). Empiler 500 prospects n'accélère rien, ça remplit la file.
- Consignez l'
execution_idreçu dans les webhooks sortants : c'est la clé pour retrouver ce parcours dans l'application. - Un utilisateur par intégration, avec le rôle minimal, et faites tourner son jeton quand il change de mains.
Il vous manque un endpoint ? Écrivez-nous depuis le chat d'assistance de l'application : ce qui est documenté et ouvert en priorité est décidé par les intégrations que les gens construisent réellement.