Passer au contenu principal

⛰️ SORTIE - Migration V1 vs V2

ℹ️ IMPORTANT : La consultation des articles liés à votre méthode d'intégration est primordiale afin de bien comprendre comment réaliser votre migration.

La V2 du webhook de sortie restructure en profondeur le payload que Pinpo envoie à vos systèmes à chaque événement. Le format V1 — avec son objet insightPinpo profondément imbriqué et ses champs HTML mélangés — est remplacé par une structure plate, lisible et directement exploitable.

Qu'est-ce qui change entre la V1 et la V2 ?

La structure est aplatie :

  • En V1, les données étaient enfouies dans un objet insightPinpo imbriqué sur plusieurs niveaux (insightPinpo.report.qualificationForm[0].formResponse.value).

  • En V2, les informations essentielles sont accessibles au premier niveau du payload (segmentation, engaged, formFields).

La conversation est structurée :

  • En V1, la conversation n'était disponible qu'en HTML brut (conversationDisplay).

  • En V2, elle est fournie sous forme d'un tableau d'objets typés (conversation) avec sender, content et timestamp, en plus du rendu HTML.
    ​

Les données extraites sont simplifiées :

  • Le qualificationForm V1 (avec formQuestion, formResponse, state, lastModifiedAt) est remplacé

  • en V2 par un tableau formFields avec trois champs : key, label, value.

Les champs de synthèse sont séparés :

  • En V1, le champ resume contenait un bloc HTML mélangeant segmentation, données extraites et conversation.

  • En V2, chaque élément dispose de son propre champ structuré et de sa propre version HTML (segmentationDisplay, formFieldsDisplay, conversationDisplay).

De nouveaux champs apparaissent :

  • La V2 ajoute l'objet qualification avec les dates du cycle de vie, le canal utilisé, les champs personnalisés, les tags et un lien direct vers le détail de la qualification.

Comment comprendre le format du JSON V2 par rapport au JSON V1 ?

Identification et métadonnées

Champ V1

Champ V2

Notes

id

qualification.id

Déplacé dans l'objet qualification.

externalId

qualification.externalId

Déplacé dans l'objet qualification.

insightPinpo.idScenario

agentId

Renommé. L'identifiant de l'agent qui a traité la qualification. La liste des {{agentId}} est présente sur la plateforme PINPO (Intégrations>Connexion via API KEY > ID de l’agent)

insightPinpo.trigger

eventType

Renommé. Les valeurs changent : "lead:change" → "done" (et d'autres valeurs selon l'événement).

insightPinpo.requestDate

qualification.importedAt

Renommé. Date d'import du contact.

insightPinpo.lastActivity

qualification.lastActivityAt

Renommé. Date de la dernière activité.

(n'existait pas)

qualification.engagedAt

Nouveau. Date d'envoi du premier message.

(n'existait pas)

qualification.channel

Nouveau. Canal utilisé (sms, whatsapp).

(n'existait pas)

qualification.source

Nouveau. Source marketing du contact.

(n'existait pas)

qualification.customFields

Nouveau. Les champs personnalisés transmis lors de l'import.

(n'existait pas)

qualification.test

Nouveau. Booléen indiquant s'il s'agit d'une qualification de test.

insightPinpo.report.conversationUrl

detailsUrl

Renommé. Lien direct vers le détail de la qualification dans Pinpo.

Segmentation et statut

Champ V1

Champ V2

Notes

leadTypology.key

segmentation

Simplifié. Valeur directe : "hot", "warm", "cold".

leadTypology.value

segmentationDisplay

Simplifié. Chaîne lisible avec emoji : "🔥 Hot".

insightPinpo.details[engaged]

engaged

Remonté au premier niveau. Booléen directement accessible.

insightPinpo.details[opportunity]

(supprimé)

Ce champ n'existe plus en V2.

insightPinpo.details[endedConversation]

(supprimé)

Remplacé par eventType: "done" qui indique la fin de traitement.

insightPinpo.details[qualificationStatus]

(supprimé)

Remplacé par eventType.

insightPinpo.insights

(supprimé)

Les données emoji sont intégrées dans segmentationDisplay.

resume

summary

Le bloc HTML monolithique est remplacé par une chaîne de texte concise (par exemple : "Contact engaged - Hot - 10 data extracted").

(n'existait pas)

tags

Nouveau. Tags attribués à la qualification (par exemple : "landline-phone").

(n'existait pas)

tagsDisplay

Nouveau. Version lisible des tags.

Données extraites

Champ V1

Champ V2

Notes

formQuestion.id

key

Renommé.

formQuestion.question

label

Renommé. Le libellé lisible de la donnée.

formResponse.value

value

Simplifié. La valeur directe, sans objet intermédiaire.

formQuestion.type

(supprimé)

Le type n'est plus transmis.

state

(supprimé)

L'état de modification n'est plus transmis.

lastModifiedAt

(supprimé)

La date de modification par champ n'est plus transmise.

note

(supprimé)

insightPinpo.report.qualificationFormDisplay

formFieldsDisplay

Renommé. Le rendu HTML des données extraites.

Avant (V1) — insightPinpo.report.qualificationForm :

[
  {
    "formQuestion": {
      "id": "project",
      "question": "Le lead a un projet d'achat dans le neuf",
      "type": "boolean"
    },
    "formResponse": {
      "value": true
    },
    "note": null,
    "state": "add",
    "lastModifiedAt": "2024-03-04T10:02:00.000Z"
  }
]

Après (V2) — formFields :

[
  {
    "key": "budget",
    "label": "Budget",
    "value": "10000"
  }
]

Conversation

Avant (V1) : La conversation n'était disponible qu'en HTML brut dans insightPinpo.report.conversationDisplay.
​Après (V2) : La conversation est fournie sous deux formes; et le foramat HTML ou Texte est paramétrable via la liste des Webhooks dans la plateforme PINPO.

{
  "conversation": [
    {
      "sender": "agent",
      "content": "Hello, I'm Alicia from Pinpo...",
      "timestamp": "2026-02-23T10:05:00.000Z"
    },
    {
      "sender": "contact",
      "content": "Yes, I want to know more about it",
      "timestamp": "2026-02-23T10:06:00.000Z"
    }
  ],
  "conversationDisplay": "<ul>...</ul>"
}

Contact

Champ V1

Champ V2

Notes

contact.firstName

contact.firstname

Casse modifiée.

contact.lastName

contact.lastname

Casse modifiée.

contact.phone

contact.phone

Format international avec + en V2.

contact.mail

contact.mail

Inchangé.

contact.externalId

contact.externalId

Inchangé.

contact.gender

contact.gender

Inchangé.

(n'existait pas)

contact.whatsAppUserId

Nouveau. Identifiant WhatsApp du contact, si le canal utilisé est WhatsApp.

Commercial (salesperson)

Champ V1

Champ V2

Notes

(non présent en V1 sortie)

salesperson.firstname

Nouveau. Les données du commercial sont désormais incluses dans le payload de sortie.

salesperson.lastname

salesperson.gender

salesperson.mail

salesperson.phone

salesperson.externalId

salesperson.iCalUrl

Champs supprimés en V2

Champ V1

Raison de la suppression

resume

Remplacé par summary (texte concis) + les champs *Display séparés.

insightPinpo (objet complet)

Éclaté en champs de premier niveau (segmentation, engaged, eventType, formFields, conversation, etc.).

insightPinpo.details

Les données sont directement accessibles (engaged, segmentation, eventType).

insightPinpo.insights

Intégré dans segmentationDisplay.

insightPinpo.report.qualificationForm[].state

L'état de modification par champ n'est plus suivi.

insightPinpo.report.qualificationForm[].lastModifiedAt

Idem.

insightPinpo.report.qualificationForm[].note

Les notes par champ ne sont plus transmises.

insightPinpo.report.notes

Idem.

Exemple complet : Avant / Après

Payload de sortie V1

{
  "id": "SNkSwfNMIV",
  "externalId": "externalIdLead",
  "leadTypology": {
    "key": "hot-with-meeting",
    "value": "Chaud avec RDV"
  },
  "contact": {
    "firstName": "John",
    "lastName": "Doe",
    "phone": "33606060606",
    "mail": "[email protected]",
    "externalId": "abcd1234",
    "gender": "male"
  },
  "resume": "Lead chaud

<b><u>Détail Insight PINPO</u></b>:...", "insightPinpo": { "details": [ { "key": "engaged", "title": "Engagé", "value": true }, { "key": "temperature", "title": "Température", "value": { "key": "hot", "display": "Lead chaud", "emoji": { "..." } } }, { "key": "qualificationStatus", "title": "Statut de la qualification", "value": "Clôturée" } ], "report": { "qualificationForm": [ { "formQuestion": { "id": "project", "question": "Le lead a un projet d'achat dans le neuf", "type": "boolean" }, "formResponse": { "value": true }, "state": "add", "lastModifiedAt": "2024-03-04T10:02:00.000Z" } ], "qualificationFormDisplay": "<ul>...</ul>", "conversationDisplay": "<ul>...</ul>", "conversationUrl": "https://pinpo.app.link/e/ezSMLbOx4Bb" }, "lastActivity": "2023-08-07T14:55:05.195Z", "requestDate": "2023-08-07T12:54:12.425Z", "trigger": "lead:change", "idScenario": "idAssistant" } }

Payload de sortie V2 équivalent

{
  "agentId": "c74a151b-cc63-428f-a96a-7cfe3c9c578d",
  "eventType": "done",
  "engaged": true,
  "summary": "Contact engaged - Hot - 3 data extracted",
  "segmentation": "hot",
  "segmentationDisplay": "🔥 Hot",
  "tags": "",
  "tagsDisplay": "",
  "conversation": [
    {
      "sender": "agent",
      "content": "Bonjour, je suis Marion, Cheffe de projet chez ...",
      "timestamp": "2023-08-07T12:54:00.000Z"
    },
    {
      "sender": "contact",
      "content": "Oui",
      "timestamp": "2023-08-07T14:39:00.000Z"
    }
  ],
  "conversationDisplay": "<ul>...</ul>",
  "formFields": [
    { "key": "project", "label": "Le lead a un projet d'achat dans le neuf", "value": "true" },
    { "key": "optinContact", "label": "Mise en relation souhaitée avec un conseiller", "value": "true" },
    { "key": "buyHorizon", "label": "Horizon d'achat", "value": "3 mois" },
    { "key": "meetingTime", "label": "Date et heure du RDV", "value": "2024-03-05T10:00:00.000Z" }
  ],
  "formFieldsDisplay": "<ul>...</ul>",
  "qualification": {
    "id": "SNkSwfNMIV",
    "externalId": "externalIdLead",
    "source": "",
    "customFields": {},
    "test": false,
    "channel": "sms",
    "importedAt": "2023-08-07T12:54:12.425Z",
    "engagedAt": "2023-08-07T12:54:00.000Z",
    "lastActivityAt": "2023-08-07T14:55:05.195Z"
  },
  "contact": {
    "firstname": "John",
    "lastname": "Doe",
    "gender": "male",
    "phone": "+33606060606",
    "mail": "[email protected]",
    "externalId": "abcd1234"
  },
  "salesperson": {},
  "detailsUrl": "https://app.pinpo.ai/app/qualifications?tab=qualifications&detail=SNkSwfNMIV"
}

Checklist de migration (sortie)

Étape

Action

1

Remplacer la lecture de leadTypology.key par segmentation.

2

Remplacer insightPinpo.details[engaged].value par engaged (premier niveau).

3

Remplacer insightPinpo.trigger par eventType.

4

Remplacer insightPinpo.idScenario par agentId.

5

Remplacer la lecture de insightPinpo.report.qualificationForm par formFields.

6

Adapter le parsing : formQuestion.question → label (in formFields), formResponse.value → value (in formFields).

7

Remplacer insightPinpo.report.conversationDisplay par le tableau structuré conversationDisplay.

8

Remplacer id et externalId (racine) par qualification.id et qualification.externalId.

9

Remplacer insightPinpo.requestDate par qualification.importedAt.

10

Remplacer insightPinpo.lastActivity par qualification.lastActivityAt.

11

Remplacer insightPinpo.report.conversationUrl par detailsUrl.

12

Adapter la casse des champs contact : firstName → firstname, lastName → lastname.

13

Supprimer tout parsing de resume et utiliser summary + les champs *Display séparés.

14

Supprimer toute dépendance à insightPinpo — cet objet n'existe plus.

15

Intégrer les nouveaux champs si pertinent : qualification.channel, qualification.engagedAt, tags, contact.whatsAppUserId, salesperson.

16

Le tag unreachable ou l’insight not-responding sont remplacé par la segmentation unreachable.

17

Le tag wrong-phone devient une réponse booléenne dans le formFields (consulter les données dans Agents<Automatisations pour récupérer la bonne key).

18

Le tag rgpd devient une réponse booléenne dans le formFields (consulter les données dans Agents<Automatisations pour récupérer la bonne key).

19

Le tag opt-out devient une réponse booléenne dans le formFields (consulter les données dans Agents<Automatisations pour récupérer la bonne key).

Conseil : Si vous faisiez du parsing du HTML du champ resume ou de conversationDisplay pour en extraire des données, la V2 vous simplifie la vie. Les données structurées (conversation, formFields, segmentation) vous évitent tout parsing HTML. Profitez de la migration pour supprimer ce code fragile.


🌟 - Bonnes pratiques

  • Utilisez les champs structurés plutôt que les champs **Display**. Les champs conversation, formFields et segmentation sont faits pour être traités programmatiquement. Les champs Display (HTML) sont destinés à l'affichage humain uniquement.

  • Ne cherchez pas l'objet **insightPinpo**. Il n'existe plus. Si votre code y fait référence, il cassera. Faites un rechercher/remplacer global dans votre codebase.

  • Exploitez les nouvelles dates. Les champs importedAt, engagedAt et lastActivityAt vous permettent de mesurer les délais de traitement sans calcul supplémentaire.

  • Gérez le champ **salesperson** en sortie. En V1, les données du commercial n'étaient pas renvoyées dans le webhook. En V2, elles le sont. Utilisez-les pour router la qualification vers le bon commercial dans votre CRM.

  • Testez avec un webhook de test. Configurez un webhook pointant vers un outil de capture (comme webhook.site ou un endpoint Make/Zapier temporaire) pour visualiser le payload V2 complet avant d'adapter votre code de production.

  • Migrez entrée et sortie en même temps. Si vous migrez l'API d'import (entrée) en V2, migrez le webhook (sortie) dans le même mouvement. Maintenir un mix V1/V2 entre entrée et sortie complexifie inutilement votre intégration.

Avez-vous trouvé la réponse à votre question ?