Aperçu

Les webhooks fournissent des notifications en temps réel lorsque des événements se produisent dans votre espace de travail OpenPhone, permettant des intégrations puissantes et des workflows automatisés. Configurez les webhooks pour envoyer instantanément les données d’événements à vos applications lorsque les appels se terminent, que des messages arrivent ou que des contacts sont modifiés.

Comment fonctionnent les webhooks

Notifications pilotées par les événements : Les webhooks délivrent des notifications instantanées lorsque des événements spécifiques se produisent dans votre espace de travail OpenPhone. Quand un événement se déclenche, OpenPhone envoie une requête HTTP POST à l’URL que vous avez spécifiée avec les données détaillées de l’événement. Flux de travail type :
  1. Configurer l’URL du webhook et sélectionner les types d’événements
  2. OpenPhone surveille les événements spécifiés
  3. Lorsqu’un événement se produit, OpenPhone envoie une requête POST avec les données de l’événement
  4. Votre application traite les données de l’événement et répond
  5. OpenPhone enregistre le statut de livraison et réessaie si nécessaire
La configuration des webhooks nécessite les permissions Propriétaire ou Administrateur de l’espace de travail. Les paramètres sont gérés uniquement via les applications web et de bureau.
Interface de configuration des webhooks :
Formulaire de création de webhook dans les paramètres OpenPhone

Événements webhook disponibles

Événements de messagerie

Notifications de messages texte :
  • message.received : Message texte reçu par le numéro de téléphone de l’espace de travail (inclut les pièces jointes multimédias)
  • message.delivered : Message texte envoyé depuis l’espace de travail et livré avec succès (inclut les médias)
Analyses basées sur l’IA :
  • call.summary.completed : Résumé d’appel généré par l’IA disponible dans les données de l’événement
  • call.transcript.completed : Transcription complète de l’appel disponible dans les données de l’événement

Événements vocaux

Notifications de statut d’appel :
  • call.ringing : Appel entrant reçu par le numéro de téléphone de l’espace de travail
  • call.completed : Appel terminé (répondu ou non répondu, peut inclure un message vocal)
  • call.recording.completed : Enregistrement d’appel disponible à l’URL fournie

Événements de contact

Gestion des contacts :
  • contact.updated : Contact créé ou modifié dans l’espace de travail
  • contact.deleted : Contact supprimé de l’espace de travail

Configuration des webhooks

Prérequis de configuration

Paramètres obligatoires :
ParamètreExigenceDescription
URLObligatoirePoint de terminaison du gestionnaire de webhook (HTTPS fortement recommandé en production)
Types d’événementsObligatoireSélectionnez un ou plusieurs types d’événements à surveiller
RessourcesObligatoireChoisissez les numéros de téléphone (appels/messages) ou les utilisateurs/groupes (contacts) à surveiller
Paramètres facultatifs :
ParamètreDescription
LibelléNom descriptif pour l’identification et la gestion du webhook

Procédure de configuration

Pour créer un webhook :
  1. Accédez à ParamètresWebhooks dans OpenPhone
  2. Cliquez sur Créer un webhook
  3. Saisissez l’URL de votre gestionnaire de webhook
  4. Sélectionnez les types d’événements à surveiller
  5. Choisissez les numéros de téléphone ou les ressources de contact
  6. Ajoutez un libellé facultatif pour l’identification
  7. Enregistrez et testez la configuration

Construction de gestionnaires de webhook

Exigences du gestionnaire

Spécifications techniques :
  • Accepter les requêtes HTTP POST sur votre URL de webhook
  • Traiter la charge utile JSON de l’événement dans le corps de la requête
  • Répondre avec un code de statut HTTP 2xx sous 10 secondes
  • Vérifier la signature du webhook pour la sécurité
  • Gérer les nouvelles tentatives et les échecs de manière élégante
Gestion des réponses :
  • Succès : Renvoyer un code de statut 2xx (aucun corps de réponse requis)
  • Échec : Une réponse non-2xx déclenche la séquence de nouvelle tentative d’OpenPhone
  • Délai d’expiration : Aucune réponse sous 10 secondes déclenche les nouvelles tentatives
Flexibilité de développement : Les gestionnaires de webhook peuvent être développés dans n’importe quel langage de programmation qui prend en charge les requêtes et réponses HTTP. Déployez sur des plateformes cloud, des serveurs ou des fonctions sans serveur.

Sécurité et authentification

Vérification de signature de webhook : Tous les appels de webhook incluent des signatures cryptographiques pour vérifier l’authenticité et prévenir les attaques d’usurpation. Format d’en-tête de signature :
'openphone-signature': 'hmac;1;1639710054089;mw1K4fvh5m9XzsGon4C5N3KvL0bkmPZSAy
b/9Vms2Qo='
Structure de signature :
<schéma>;<version>;<horodatage>;<signature>
ComposantDescriptionValeur actuelle
schémaAlgorithme de signatureToujours « hmac »
versionVersion de signatureToujours « 1 »
horodatageHeure de génération de signatureHorodatage Unix
signatureSignature HMAC encodée en Base64Condensé SHA256

Processus de vérification de signature

Étapes de vérification :
  1. Extraire les composants de l’en-tête openphone-signature
  2. Préparer les données signées en concaténant horodatage + "." + charge_utile
  3. Décoder la clé de signature depuis base64 (disponible dans les détails du webhook)
  4. Calculer HMAC-SHA256 en utilisant la clé décodée et les données signées
  5. Comparer le résultat avec la signature de l’en-tête
Exigences importantes :
  • Supprimer tous les espaces et sauts de ligne de la charge utile JSON avant la concaténation
  • Utiliser la forme binaire de la clé de signature décodée en base64 pour le calcul HMAC
  • Assurer une correspondance exacte des chaînes pour que la vérification réussisse
Obtenir votre clé de signature :
  1. Aller sur la page de détails du webhook dans OpenPhone
  2. Cliquer sur les points de suspension (⋯) en haut à droite
  3. Sélectionner « Révéler le secret de signature »
  4. Copier la clé encodée en base64 pour votre application

Exemples d’implémentation

Gestionnaire de webhook Node.js :
const express = require("express")
const bodyParser = require('body-parser')
const crypto = require('node:crypto');

const app = express()
const port = 8000

app.use(bodyParser.json())

app.post("/", function (req, res) {
  // signingKey provient de « Révéler le secret de signature » dans l'application OpenPhone.
  const signingKey = 'R2ZLM2o0bFhBNVpyUnU2NG9mYXQ1MHNyR3pvSUhIVVg='

  // Analyser les champs de l'en-tête openphone-signature.
  const signature = req.headers['openphone-signature']
  const fields = signature.split(';')
  const timestamp = fields[2]
  const providedDigest = fields[3]

  // Calculer les données couvertes par la signature.
  const signedData = timestamp + '.' + JSON.stringify(req.body)

  // Convertir la clé de signature encodée en base64 en binaire.
  const signingKeyBinary = Buffer.from(signingKey, 'base64').toString('binary')

  // Calculer le condensé HMAC SHA256.
  const computedDigest = crypto.createHmac('sha256',signingKeyBinary)
    .update(Buffer.from(signedData,'utf8'))
    .digest('base64')

  // Vérifier que la signature correspond
  if (providedDigest === computedDigest) {
    console.log(`vérification de signature réussie`)
    // Traiter l'événement webhook ici
  } else {
    console.log(`vérification de signature échouée`)
    return res.status(401).send('Unauthorized')
  }

  res.send({})
});

app.listen(port, function () {
  console.log(`serveur webhook en écoute sur le port ${port}`)
})
Gestionnaire de webhook Python :
import base64
import hmac
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_webhook_call():
    # signingKey provient de "Reveal Signing Secret" dans l'application OpenPhone.
    signing_key = 'R2ZLM2o0bFhBNVpyUnU2NG9mYXQ1MHNyR3pvSUhIVVg='

    # Parser les champs de l'en-tête openphone-signature.
    signature = request.headers['openphone-signature']
    fields = signature.split(';')
    timestamp = fields[2]
    provided_digest = fields[3]

    # Calculer les données couvertes par la signature sous forme d'octets.
    signed_data_bytes = b''.join([timestamp.encode(), b'.', request.data])

    # Convertir la clé de signature encodée en base64 en octets.
    signing_key_bytes = base64.b64decode(signing_key)

    # Calculer le condensé HMAC SHA256.
    hmac_object = hmac.new(signing_key_bytes, signed_data_bytes, 'sha256')
    computed_digest = base64.b64encode(hmac_object.digest()).decode()

    # Vérifier que la signature correspond
    if provided_digest == computed_digest:
        print('vérification de signature réussie')
        # Traiter l'événement webhook ici
    else:
        print('échec de la vérification de signature')
        return jsonify({'error': 'Unauthorized'}), 401
        
    return jsonify({})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)
Les versions futures pourront inclure plusieurs signatures séparées par des virgules. Séparez la valeur de l’en-tête sur les virgules pour gérer plusieurs signatures si nécessaire.

Bonnes pratiques de sécurité

Protection contre les attaques par rejeu : Implémentez la validation d’horodatage pour prévenir les attaques par rejeu :
  • Comparez l’horodatage de la signature avec l’heure actuelle
  • Rejetez les requêtes avec des horodatages en dehors de la tolérance acceptable (par ex. 5 minutes)
  • Chaque appel webhook génère un horodatage et une signature uniques
  • Les nouvelles tentatives incluent automatiquement de nouveaux horodatages
Mesures de sécurité supplémentaires :
  • Utilisez toujours des URL HTTPS pour les webhooks en production
  • Stockez les clés de signature de manière sécurisée (variables d’environnement, gestion des secrets)
  • Implémentez une gestion d’erreurs et une journalisation appropriées
  • Envisagez la limitation de débit pour les endpoints webhook

Gestion des erreurs et nouvelles tentatives

Système de nouvelle tentative automatique

Comportement des nouvelles tentatives :
  • Conditions de déclenchement : Codes de réponse non-2xx ou délai d’expiration de 10 secondes
  • Stratégie de temporisation : Temporisation exponentielle avec délais croissants
  • Durée des nouvelles tentatives : Jusqu’à 3 jours de tentatives de relance
  • Échec final : Notification par courriel envoyée au créateur du webhook
Chronologie des nouvelles tentatives :
  • Les premières tentatives se produisent rapidement pour minimiser les délais
  • Les délais augmentent exponentiellement à chaque tentative
  • Privilégie la livraison proche du moment de l’événement d’origine
  • Suivi automatique du statut tout au long du processus

Options de nouvelle tentative manuelle

Gestion des webhooks :
  • Consulter le statut de livraison dans les détails du webhook OpenPhone
  • Relancer manuellement les appels de webhook échoués à tout moment
  • Les webhooks échoués sont marqués avec le statut « failure »
  • Les nouvelles tentatives manuelles réussies mettent à jour le statut vers « success »

Notifications d’échec

Lorsque les nouvelles tentatives sont épuisées :
  • Alerte par courriel envoyée au créateur du webhook
  • L’appel de webhook est marqué comme définitivement échoué
  • Les détails de l’événement sont conservés pour révision manuelle
  • Possibilité de relancer manuellement une fois les problèmes résolus

Tests et validation

Tests de développement

Options de tests locaux :
  • Clients HTTP : Utilisez cURL, Postman ou Insomnia pour envoyer des requêtes POST de test
  • URLs locales : Testez sur localhost pendant le développement
  • Données fictives : Créez des exemples de JSON d’événements correspondant au format OpenPhone
  • Tests de signature : Vérifiez la logique de validation HMAC avec des clés de test

Fonctionnalités de test OpenPhone

Requête de test intégrée :
  1. Accédez à la page de détails du webhook dans OpenPhone
  2. Cliquez sur les points de suspension (⋯) en haut à droite
  3. Sélectionnez « Send Test Request »
  4. OpenPhone envoie un événement d’exemple à votre URL de webhook
  5. Vérifiez la validation de signature et la gestion des réponses
Les requêtes de test nécessitent des URLs de webhook accessibles publiquement. Les URLs de développement local ne fonctionneront pas avec la fonctionnalité de test d’OpenPhone.

Tests d’événements en temps réel

Déclencheurs d’événements réels :
  • Configurez le webhook pour un type d’événement spécifique (par exemple, message.received)
  • Sélectionnez votre numéro OpenPhone dans les ressources du webhook
  • Déclenchez un événement réel (envoyez un SMS à votre numéro)
  • Surveillez la livraison et le traitement du webhook
  • Vérifiez le bon fonctionnement de bout en bout
Liste de contrôle des tests :
  • Le webhook reçoit les requêtes POST correctement
  • La vérification de signature fonctionne
  • L’analyse des données d’événement réussit
  • La logique applicative traite les événements correctement
  • Les réponses d’erreur déclenchent des nouvelles tentatives
  • Les réponses de succès arrêtent la séquence de nouvelles tentatives

Dépannage

Problèmes courants et solutions

Problèmes de configuration :
  • Vérifiez que l’URL du webhook est correcte et accessible
  • Vérifiez que le statut du webhook est activé dans les paramètres
  • Confirmez que les types d’événements sont correctement sélectionnés
  • Validez que les ressources (numéros de téléphone/utilisateurs/groupes) sont correctes
Problèmes de réponse :
  • Codes de statut HTTP : Assurez-vous d’obtenir des réponses 2xx pour un traitement réussi
  • Délai de réponse : Renvoyez les réponses dans le délai de 10 secondes
  • Gestion d’erreurs : Implémentez des réponses d’erreur appropriées pour le débogage
Vérification de sécurité :
  • Validation de signature : Vérifiez le calcul de la signature HMAC
  • Format de clé : Assurez-vous que la clé de signature est correctement décodée en base64
  • Gestion des horodatages : Vérifiez l’extraction et la concaténation des horodatages

Outils de débogage

Surveillance des événements :
  • Journal des événements : Consultez l’historique de livraison dans la page de détails du webhook
  • Suivi du statut : Surveillez les taux de succès/échec
  • Tentatives de relance : Examinez les séquences de relance automatiques
  • Tests manuels : Utilisez « Envoyer une requête de test » pour une validation immédiate
Optimisation des performances :
  • Temps de réponse : Maintenez le traitement sous 10 secondes
  • Taux d’erreur : Minimisez les échecs pour réduire la surcharge de relances
  • Journalisation : Implémentez une journalisation complète des requêtes/réponses
  • Surveillance : Configurez des alertes pour les échecs de livraison de webhook

Exemples de payload d’événement

Événements de message

Payload message.received :
{  
  "id": "EVc67ec998b35c41d388af50799aeeba3e",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-23T16:55:52.557Z",  
  "type": "message.received",  
  "data": {  
    "object": {  
      "id": "AC24a8b8321c4f4cf2be110f4250793d51",  
      "object": "message",  
      "from": "+14155550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "body": "Hello",  
      "media": [  
        {  
         "url": "https://storage.googleapis.com/opstatics-dev/6c908000ada94d9fb206649ecb8cc928",  
         "type": "image/jpeg"  
        }  
      ],  
      "status": "received",  
      "createdAt": "2022-01-23T16:55:52.420Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
Payload message.delivered :
{  
  "id": "EVdefd85c2c3b740429cf28ade5b69bcba",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-23T17:05:56.220Z",  
  "type": "message.delivered",  
  "data": {  
    "object": {  
      "id": "ACcdcc2668c4134c3cbfdacb9e273cac6f",  
      "object": "message",  
      "from": "+13105550199",  
      "to": "+14155550100",  
      "direction": "outgoing",  
      "body": "Have a nice day",  
      "media": [  
        {  
         "url": "https://opstatics-dev.s3.amazonaws.com/i/ab6084db-5259-42c0-93c1-e17fb2628567.jpeg",  
         "type": "image/jpeg"  
        }  
      ],  
      "status": "delivered",  
      "createdAt": "2022-01-23T17:05:45.195Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}

Événements d’appel

Payload call.ringing :
{  
  "id": "EV95c3708f9112412a834cc8d415470cd8",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-23T17:07:51.454Z",  
  "type": "call.ringing",  
  "data": {  
    "object": {  
      "id": "ACbaee66e137f0467dbed5ad4bc8d60800",  
      "object": "call",  
      "from": "+14155550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "media": [],  
      "voicemail": null,  
      "status": "ringing",  
      "createdAt": "2022-01-23T17:07:51.116Z",  
      "answeredAt": null,  
      "completedAt": null,  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
call.completed (entrant avec messagerie vocale) :
{  
  "id": "EVd39d3c8d6f244d21a9131de4fc9350d0",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-24T19:22:25.427Z",  
  "type": "call.completed",  
  "data": {  
    "object": {  
      "id": "ACa29ee906a4e04312a6928427b1c21721",  
      "object": "call",  
      "from": "+14145550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "media": [],  
      "voicemail": {  
        "url": "https://m.openph.one/static/85ad4740be6048e4a80efb268d347482.mp3",  
        "type": "audio/mpeg",  
        "duration": 7  
      },  
      "status": "completed",  
      "createdAt": "2022-01-24T19:21:59.545Z",  
      "answeredAt": null,  
      "completedAt": "2022-01-24T19:22:19.000Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
call.completed (appel sortant répondu) :
{  
  "id": "EV348de11e4b134fa48017ac45a251dd3e",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-24T19:28:45.370Z",  
  "type": "call.completed",  
  "data": {  
    "object": {  
      "id": "AC7ab6f57e62924294925d0ea961de7dc5",  
      "object": "call",  
      "from": "+13105550199",  
      "to": "+14155550100",  
      "direction": "outgoing",  
      "media": [],  
      "voicemail": null,  
      "status": "completed",  
      "createdAt": "2022-01-24T19:28:33.892Z",  
      "answeredAt": "2022-01-24T19:28:42.000Z",  
      "completedAt": "2022-01-24T19:28:45.000Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}
Payload call.recording.completed :
{  
  "id": "EVda6e196255814311aaac1983005fa2d9",  
  "object": "event",  
  "apiVersion": "v2",  
  "createdAt": "2022-01-24T19:30:55.400Z",  
  "type": "call.recording.completed",  
  "data": {  
    "object": {  
      "id": "AC0d3b9011efa041d78c864019ad9e948c",  
      "object": "call",  
      "from": "+14155550100",  
      "to": "+13105550199",  
      "direction": "incoming",  
      "media": [  
        {   
          "url": "https://storage.googleapis.com/opstatics-dev/b5f839bc72a24b33a7fc032f78777146.mp3",  
          "type": "audio/mpeg",  
          "duration": 7  
        }  
      ],  
      "voicemail": null,  
      "status": "completed",  
      "createdAt": "2022-01-24T19:30:34.675Z",  
      "answeredAt": "2022-01-24T19:30:38.000Z",  
      "completedAt": "2022-01-24T19:30:48.000Z",  
      "userId": "USu5AsEHuQ",  
      "phoneNumberId": "PNtoDbDhuz",  
      "conversationId": "CN78ba0373683c48fd8fd96bc836c51f79"  
    }  
  }  
}

Événements de contact

Payload contact.updated et contact.deleted :
{  
  "id": "EVe844e47e9fa4494d9acfa1144839ed94",  
  "object": "event",  
  "createdAt": "2022-01-24T19:44:09.579Z",  
  "apiVersion": "v2",  
  "type": "contact.updated",  
  "data": {  
    "object": {  
      "id": "CT61eeff33f3b14cfe6358cb52",  
      "object": "contact",  
      "firstName": "Jane",  
      "lastName": "Smith",  
      "company": "Comp Inc",  
      "role": "Agent",  
      "pictureUrl": null,  
      "fields": [  
         {  
           "name": "Phone",  
           "type": "phone-number",  
           "value": "+14155551212"  
         },  
         {  
           "name": "Email",  
           "type": "email",  
           "value": null  
         },  
         {  
           "name": "Prop1",  
           "type": "string",  
           "value": "Value12"  
         }  
      ],  
      "notes": [  
        {  
          "text": "@USu5AsEHuQ mynote 😂",  
          "enrichment": {  
            "taggedIds": {   
              "groupIds": [],   
              "userIds": [  
                "USu5AsEHuQ"  
              ],  
              "orgIds": []  
            },  
            "tokens": {   
              "USu5AsEHuQ": {   
                "token": "USu5AsEHuQ",   
                "replacement": "Tom Smith",   
                "type": "mention",   
                "locations": [  
                  {   
                    "startIndex": 1,   
                    "endIndex": 11   
                  }  
                ]   
              }   
            }   
          },  
          "createdAt": "2022-01-24T19:35:38.323Z",  
          "updatedAt": "2022-01-24T19:35:38.323Z",  
          "userId": "USu5AsEHuQ"  
        }  
      ],  
      "sharedWith": [  
        "USu5AsEHuQ"  
       ],  
      "createdAt": "2022-01-24T19:35:38.318Z",  
      "updatedAt": "2022-01-24T19:44:09.565Z",  
      "userId": "USu5AsEHuQ"  
    }  
  }  
}

Événements d’analyse IA

Payload call.summary.completed :
{  
  "id": "EVc86d16fed5314cf6bd7bf13f11c65fd2",  
  "object": "event",  
  "apiVersion": "v3",  
  "createdAt": "2024-09-05T15:30:24.213Z",  
  "type": "call.summary.completed",  
  "data": {  
    "object": {  
      "object": "callSummary",  
      "callId": "AC641569d25e0a4489ad807409d9bee3fd",  
      "status": "completed",  
      "summary": [  
        "ceci est le résumé de votre appel."  
      ],  
      "nextSteps": [  
        "voici vos prochaines étapes."  
      ]  
    }  
  }  
}
Payload call.transcript.completed :
{  
  "id": "EV67e3564088bf4badb6593cd7db8f2832",  
  "object": "event",  
  "apiVersion": "v3",  
  "createdAt": "2024-09-05T15:30:18.629Z",  
  "type": "call.transcript.completed",  
  "data": {  
    "object": {  
      "object": "callTranscript",  
      "callId": "AC741569d25e0a4489ad804709d9bee3fc",  
      "createdAt": "2024-09-05T15:30:18.198Z",  
      "dialogue": [  
        {  
          "end": 73.57498,  
          "start": 0.03998,  
          "userId": "UStpTEr9a6",  
          "content": "bonjour tout le monde",  
          "identifier": "+12345678901"  
        },  
        {  
          "end": 1.52,  
          "start": 1.12,  
          "userId": "USiAYGldYE",  
          "content": "salut",  
          "identifier": "+19876543210"  
        }  
      ],  
      "duration": 87.614685,  
      "status": "completed"  
    }  
  }  
}

Ressources supplémentaires

Besoin d’aide ? Soumettez une demande d’assistance sur support.openphone.com pour obtenir de l’aide technique concernant l’implémentation des webhooks.