Souscriptions aux webhooks de campagne
Les souscriptions envoient les événements d'une campagne d'Alvio vers votre CRM ou votre système. Vous choisissez une destination HTTPS et les événements qu'elle doit recevoir. Pour envoyer des contacts dans l'autre sens, consultez le guide d'intégration webhook.
Une destination contient l'URL, l'authentification et les paramètres de livraison. Elle est réutilisable entre les campagnes rattachées au même numéro professionnel. Une souscription associe cette destination à un événement d'une campagne. Créer la destination seule n'active pas les notifications.
Configurer une souscription
- Ouvrez votre campagne, puis l'onglet Webhooks et la vue Setup.
- Dans Destination name et Destination URL, indiquez un nom et votre URL de réception, par exemple
https://crm.example.com/webhooks/alvio. - Choisissez l'authentification attendue par votre serveur : Bearer token, API key header, ou No authentication. Pour une clé API, renseignez le nom de l'en-tête et sa valeur ; le nom proposé est
X-API-Key. - Renseignez éventuellement Optional signing secret pour signer les requêtes, puis choisissez Retry policy : aucun réessai, un réessai ou jusqu'à trois réessais. Trois réessais sont proposés par défaut.
- Cliquez sur Create endpoint, puis sélectionnez la destination dans Available endpoints. Vous pouvez aussi sélectionner une destination déjà créée pour ce numéro.
- Choisissez les événements de la famille Lead updates (
event_type: "update"), consultez leur aperçu JSON et cliquez sur Attach to campaign.LEAD_RECEIVEDetMEETING_BOOKEDsont présélectionnés ; adaptez ce choix à votre intégration. - Testez la réception avec la procédure ci-dessous, puis utilisez History pour suivre les livraisons.
La création depuis cet écran configure des requêtes POST avec un corps JSON. La clé d'authentification de votre destination est indépendante du jeton x-api-key utilisé pour faire entrer des contacts dans Alvio.
Les souscriptions existantes peuvent être activées, désactivées ou supprimées. Désactiver conserve l'historique, mais les livraisons en attente traitées pendant la désactivation passent en échec ; réactiver ne les renvoie pas automatiquement. Supprimer une souscription supprime aussi son historique de livraison. Préférez la désactivation si vous souhaitez le conserver.
Événements disponibles
Les événements suivants utilisent event_type: "update". La souscription ne déclenche pas elle-même l'action métier : elle reçoit une notification lorsque la campagne produit l'événement.
event_name | Déclencheur | Champs utiles dans data |
|---|---|---|
NO_WHATSAPP | Le contact est identifié comme injoignable sur WhatsApp. | reason, recommended_action, error |
CONTACT_STOP_REQUESTED | Une demande confirmée d'arrêt des appels ou de tout contact est enregistrée. | scope, requested_at, evidence, source_message_id, restriction_applied |
MEETING_BOOKED | Un rendez-vous ou rappel est confirmé. | meeting_type, booking_status, start, end, timezone, assigned_to, location, notes, urgent, urgency_reason |
LEAD_RECEIVED | Un lead est accepté par le webhook d'entrée de campagne. | contact_status, sent_immediately, skipped_immediate_send, is_existing_contact, existing_conversation_reused, error_message, received_via |
MESSAGE_SENT | Un message de campagne est envoyé, quelle que soit sa position dans la séquence. | channel, step_order, step_name, message_position |
REPLY_DETECTED | Une réponse déclenche l'événement d'arrêt des relances de campagne. | pause_reason, step_order, step_name |
ESCALATION | La conversation est transférée pour un suivi humain. | reason, category, priority, assigned_to, triggered_by, escalation_event_type, urgent, urgency_reason |
SEND_FAILED | Une tentative d'envoi de campagne échoue. | reason, step_order, step_name |
MESSAGE_SENT ne confirme ni la livraison ni la lecture par le destinataire. REPLY_DETECTED n'est pas un flux de tous les messages entrants. Pour suivre les statuts delivered et read, utilisez le suivi des résultats de contacts. NO_WHATSAPP ne couvre pas toutes les causes possibles d'échec d'envoi.
Événements propres à certaines intégrations
| Événement | Type | Disponibilité |
|---|---|---|
MEETING_RESCHEDULED | update | Report confirmé dans l’agenda, pour les intégrations configurées pour gérer les reports. |
MEETING_CANCELLED | update | Annulation confirmée dans l’agenda, pour les intégrations configurées pour gérer les annulations. |
AVAILABILITY_REQUESTED | availability_request | Demande de disponibilités pour les intégrations configurées à cet effet ; absent du sélecteur standard Lead updates. |
Ne supposez pas qu'une campagne produit ces événements parce que leur nom existe. Validez leur disponibilité et leur contrat avec Alvio avant intégration. Les événements de report/annulation peuvent inclure operation_id, previous_outcome_id, outcome_id, calendar_event_id, previous_start, previous_end, start, end et timezone.
Format commun du JSON
| Champ | Contenu |
|---|---|
event_id | Identifiant de l'événement. Conservé lors des réessais et renvois ; sert à la déduplication. |
event_type | Famille, généralement update. |
event_name | Nom de l'événement décrit ci-dessus. |
version | Version du contrat : 2026-04-08. Ce n'est pas la date de livraison. |
occurred_at | Date de l'événement au format ISO 8601. |
lead | first_name, last_name, phone, email. Les valeurs indisponibles peuvent être null. |
external_references | Identifiants CRM : contact_id, deal_id, account_id, ou null. |
alvio | campaign_id, campaign_name, conversation_id, conversation_url, business_phone, has_whatsapp. Certains champs peuvent être null. |
data | Données propres à l'événement, dont les champs d'urgence lorsqu'ils sont disponibles. |
source_data | Données source associées à l'événement, sinon les champs personnalisés du contact. Peut être {}. |
Les références CRM sont recherchées dans les champs du contact, dans cet ordre : crm_contact_id, crmid, contact_id pour le contact ; crm_deal_id, dealid, deal_id pour le deal ; account_id, crm_account_id pour le compte. Transmettez des identifiants stables dès l'entrée du lead.
has_whatsapp peut être null lorsque l'information n'est pas déterminée ; ne l'interprétez pas comme false. source_data peut contenir des données personnelles : ne le journalisez pas intégralement sans nécessité. Le contrat standard n'inclut pas de transcript complet de conversation.
Rendez-vous confirmé
Exemple fictif complet : réservation enregistrée à 10 h à Paris pour 14 h le même jour, sans urgence.
{
"event_id": "11111111-1111-4111-8111-111111111001",
"event_type": "update",
"event_name": "MEETING_BOOKED",
"version": "2026-04-08",
"occurred_at": "2026-10-27T09:00:00Z",
"lead": {
"first_name": "Jane",
"last_name": "Doe",
"phone": "+33612345678",
"email": "jane@example.com"
},
"external_references": {
"contact_id": "CRM_123456",
"deal_id": "DEAL_789",
"account_id": null
},
"alvio": {
"campaign_id": "22222222-2222-4222-8222-222222222222",
"campaign_name": "Suivi prospects",
"conversation_id": "33333333-3333-4333-8333-333333333333",
"conversation_url": "https://alvio.ai/inbox/conversation/33333333-3333-4333-8333-333333333333",
"business_phone": "+33100000000",
"has_whatsapp": true
},
"data": {
"meeting_type": "appointment",
"booking_status": "confirmed",
"start": "2026-10-27T14:00:00+01:00",
"end": "2026-10-27T14:30:00+01:00",
"timezone": "Europe/Paris",
"assigned_to": null,
"location": null,
"notes": null,
"urgent": false,
"urgency_reason": null
},
"source_data": {
"crm_contact_id": "CRM_123456",
"crm_deal_id": "DEAL_789",
"source": "website"
}
}Escalade urgente
Exemple fictif complet : demande explicite de rappel immédiat, sans rendez-vous confirmé dans l'agenda.
{
"event_id": "11111111-1111-4111-8111-111111111002",
"event_type": "update",
"event_name": "ESCALATION",
"version": "2026-04-08",
"occurred_at": "2026-10-27T09:00:00Z",
"lead": {
"first_name": "Jane",
"last_name": "Doe",
"phone": "+33612345678",
"email": "jane@example.com"
},
"external_references": {
"contact_id": "CRM_123456",
"deal_id": "DEAL_789",
"account_id": null
},
"alvio": {
"campaign_id": "22222222-2222-4222-8222-222222222222",
"campaign_name": "Suivi prospects",
"conversation_id": "33333333-3333-4333-8333-333333333333",
"conversation_url": "https://alvio.ai/inbox/conversation/33333333-3333-4333-8333-333333333333",
"business_phone": "+33100000000",
"has_whatsapp": true
},
"data": {
"status": "escalated",
"reason": "Le prospect demande un rappel immédiat.",
"category": "callback_request",
"priority": null,
"assigned_to": null,
"triggered_by": null,
"escalation_event_type": null,
"urgent": true,
"urgency_reason": "immediate_request"
},
"source_data": {
"crm_contact_id": "CRM_123456",
"crm_deal_id": "DEAL_789",
"source": "website"
}
}data.urgent est le champ à utiliser pour prioriser un rappel. Une priority élevée ne suffit pas. Les règles de calcul, les valeurs possibles et le comportement des anciens événements sont détaillés dans Rendez-vous et rappels urgents. L'urgence ne garantit pas une livraison instantanée.
Demande STOP
Exemple fictif complet d'arrêt de tout contact :
{
"event_id": "11111111-1111-4111-8111-111111111003",
"event_type": "update",
"event_name": "CONTACT_STOP_REQUESTED",
"version": "2026-04-08",
"occurred_at": "2026-10-27T09:00:00Z",
"lead": {
"first_name": "Jane",
"last_name": "Doe",
"phone": "+33612345678",
"email": "jane@example.com"
},
"external_references": {
"contact_id": "CRM_123456",
"deal_id": "DEAL_789",
"account_id": null
},
"alvio": {
"campaign_id": "22222222-2222-4222-8222-222222222222",
"campaign_name": "Suivi prospects",
"conversation_id": "33333333-3333-4333-8333-333333333333",
"conversation_url": "https://alvio.ai/inbox/conversation/33333333-3333-4333-8333-333333333333",
"business_phone": "+33100000000",
"has_whatsapp": null
},
"data": {
"status": "contact_stop_requested",
"scope": "all_contact",
"requested_at": "2026-10-27T09:00:00Z",
"evidence": "STOP",
"source_message_id": "44444444-4444-4444-8444-444444444444",
"restriction_applied": true,
"urgent": false,
"urgency_reason": null
},
"source_data": {
"crm_contact_id": "CRM_123456",
"crm_deal_id": "DEAL_789",
"source": "website"
}
}scope: "all_contact"indique une demande d'arrêt de tout contact.scope: "phone_calls"indique une demande d'arrêt des appels ; ne la transformez pas en arrêt de tous les canaux.requested_atetevidencedonnent la date et le justificatif de la demande ;source_message_ididentifie le message d'origine.restriction_applied: trueindique que la restriction a été appliquée côté Alvio. Votre CRM doit encore appliquer la règle correspondante dans ses propres outils.
Le STOP est distinct d'une simple réponse du prospect. Son payload est figé avec les références CRM et la preuve de la demande. Une souscription créée après cet événement ne reçoit pas rétroactivement ce STOP.
Authentification et signature
Les requêtes contiennent Content-Type: application/json ainsi que :
| En-tête | Valeur |
|---|---|
X-Alvio-Event-Id | event_id du JSON |
X-Alvio-Event-Type | event_type du JSON |
X-Alvio-Event-Name | event_name du JSON |
Authorization | Bearer <votre jeton> si vous avez choisi Bearer token |
| En-tête API personnalisé | Votre clé si vous avez choisi API key header |
X-Alvio-Signature | Présent si un secret de signature est configuré |
La signature suit le format sha256=<empreinte hexadécimale>. Calculez un HMAC-SHA256 sur les octets UTF-8 du corps JSON brut reçu, avec le secret partagé, puis comparez les empreintes en temps constant. Vérifiez avant de parser ou reformater le JSON : changer les espaces ou l'ordre des propriétés change la signature. Rejetez une signature manquante ou invalide si votre intégration exige une signature.
La signature n'inclut pas d'horodatage de livraison distinct. Un renvoi du même événement peut être valide : combinez la vérification avec une déduplication persistante par event_id. Utilisez HTTPS et conservez les secrets côté serveur.
Livraison, réessais et doublons
- Votre endpoint doit accuser réception avec un HTTP 2xx après avoir enregistré durablement l'événement. Effectuez ensuite les traitements longs de manière asynchrone. Un statut
delivereddans Alvio signifie que votre endpoint a répondu avec succès, pas que votre CRM a terminé son traitement. - Le délai d'attente est de 5 secondes par défaut pour une destination créée depuis l'écran de campagne.
- Pour la livraison HTTP standard, une erreur réseau, un timeout ou une réponse HTTP finale hors 2xx déclenche un réessai si la politique l'autorise, y compris une erreur 4xx. Corrigez donc les erreurs d'authentification ou de payload.
- Avec 3 réessais, il y a au plus 4 tentatives dans le cycle automatique. Les délais minimaux après chaque échec sont de 1, 2 puis 4 minutes ; ils ne sont pas des heures de livraison garanties.
- La file est traitée périodiquement, avec un passage planifié chaque minute. La charge et les traitements en cours peuvent ajouter du délai. Ne supposez pas un ordre strict entre les événements.
- Le JSON stocké et son
event_idsont conservés lors des réessais et renvois. Une même notification peut donc arriver plusieurs fois.
Dans votre récepteur, associez l'identifiant de l'événement à son traitement de façon atomique. Si l'événement a déjà été accepté durablement, répondez 2xx sans répéter l'action métier. Évitez notamment de créer deux rendez-vous ou deux tâches pour le même événement. L'activation d'une souscription n'est pas un mécanisme d'export de tout l'historique passé.
Tester la réception
- Utilisez une campagne de test et une destination de test pour ne pas modifier des fiches CRM réelles.
- Sélectionnez une destination et un événement, puis consultez l'aperçu JSON. L'aperçu seul n'envoie aucune requête.
- Lancez l'envoi de test. Il effectue une vraie requête HTTP avec l'authentification et la signature de la destination. Vérifiez le code HTTP, la réponse et le temps affichés.
- Si une souscription correspondante existe déjà, le test est enregistré dans History avec le type
test. Sans souscription correspondante, le résultat est affiché mais n'est pas conservé dans cet historique. - Vérifiez côté récepteur que les identifiants CRM, la déduplication et la vérification de signature fonctionnent avant d'utiliser une destination de production.
Les tests ne sont pas tous anonymisés. Hors STOP, l'aperçu et l'envoi de test peuvent reprendre le dernier événement ou le dernier contact de la campagne, avec des valeurs d'exemple pour compléter les données. Il n'existe pas de drapeau test: true systématique dans leur JSON ; ne comptez pas sur lui pour isoler vos tests.
Le test STOP utilise un exemple sans téléphone, email, identifiant CRM ou conversation réels, avec test: true, restriction_applied: false et un event_id commençant par sample_contact_stop_. Il ne doit pas provoquer de blocage d'un contact réel.
Historique et renvoi manuel
Dans History, filtrez par événement, statut ou type (live / test), ou recherchez une valeur du payload. La vue charge les 30 dernières livraisons ; ses filtres portent sur ces éléments, pas sur tout l'historique.
| Statut | Signification |
|---|---|
pending | En attente d'une tentative initiale ou d'un réessai. |
processing | Une tentative est en cours. |
delivered | Le récepteur a répondu avec succès. |
failed | Échec sans nouveau réessai automatique prévu pour cette entrée. |
Ouvrez une ligne pour consulter son payload, sa réponse et ses erreurs. Un renvoi manuel réutilise le JSON stocké et le même event_id, avec la configuration actuelle de la destination, et ajoute une entrée d'historique liée à l'originale. Il ne recalcule ni les données du contact ni l'urgence. Les tests et renvois manuels sont des envois ponctuels, sans cycle de réessais automatiques. Un renvoi ne supprime pas un éventuel réessai encore en attente sur l'entrée d'origine.
Dépannage
| Symptôme | Vérification |
|---|---|
| Aucun événement reçu | Vérifiez la campagne et le numéro professionnel, la destination choisie, la souscription active et l'événement réellement produit. Créer la destination ne suffit pas. |
401 ou 403 du récepteur | Vérifiez le jeton ou le nom et la valeur de l'en-tête API de la destination. |
| Signature invalide | Vérifiez le secret partagé et le calcul sur le corps brut, avant toute transformation JSON. |
400 ou 422 du récepteur | Vérifiez le contrat attendu, event_name et l'acceptation des valeurs null. |
| Timeout ou erreur réseau | Vérifiez l'accessibilité de l'URL et accusez réception rapidement après stockage durable. |
Subscription or endpoint inactive | La souscription ou la destination était inactive au traitement. Réactivez la configuration nécessaire avant un renvoi contrôlé. |
| Test absent de l'historique | Vérifiez qu'une souscription correspondante existait lors du test ; contrôlez aussi les filtres et les 30 dernières entrées. |
| Doublon dans le CRM | Vérifiez la déduplication par event_id, y compris pour les renvois manuels. |
Pour un nouveau raccordement, utilisez ce contrat. Si vous recevez déjà un format spécifique avec status, crm_id ou conversation_transcript, conservez votre contrat existant et validez la migration avec Alvio ; les noms ne sont pas des alias automatiques.