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 flux de travail automatisés. Configurez les webhooks pour envoyer instantanément les données d’événement à vos applications lorsque les appels se terminent, que les messages arrivent ou que les contacts changent.

Comment fonctionnent les webhooks

Notifications déclenchées par des événements : Les webhooks fournissent des notifications instantanées lorsque des événements spécifiques se produisent dans votre espace de travail OpenPhone. Lorsqu’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 typique :
  1. Configurer l’URL du webhook et sélectionner les types d’événements
  2. OpenPhone surveille les événements spécifiés
  3. Lorsque l’é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 fait une nouvelle tentative au besoin
La configuration des webhooks nécessite les permissions de propriétaire ou d’administrateur de l’espace de travail. Les paramètres sont gérés uniquement par 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 alimentées par 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 la messagerie vocale)
  • 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

Exigences de configuration

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

Processus 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é optionnel pour l’identification
  7. Enregistrez et testez la configuration

Création de gestionnaires de webhooks

Exigences pour le gestionnaire

Spécifications techniques :
  • Accepter les requêtes HTTP POST à l’URL de votre webhook
  • Traiter la charge utile JSON de l’événement dans le corps de la requête
  • Répondre avec un code d’état HTTP 2xx dans un délai de 10 secondes
  • Vérifier la signature du webhook pour des raisons de sécurité
  • Gérer les tentatives de réessai et les échecs de façon robuste
Gestion des réponses :
  • Succès : Retourner un code d’état 2xx (aucun corps de réponse requis)
  • Échec : Une réponse autre que 2xx déclenche la séquence de réessai d’OpenPhone
  • Délai d’attente : Aucune réponse dans les 10 secondes entraîne des réessais
Souplesse de développement : Les gestionnaires de webhooks peuvent être développés dans n’importe quel langage de programmation prenant en charge les requêtes et réponses HTTP. Déployez-les sur des plateformes infonuagiques, des serveurs ou des fonctions serverless.

Sécurité et authentification

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

Processus de vérification de la signature

Étapes de vérification :
  1. Extraire les composantes de l’en-tête openphone-signature
  2. Préparer les données à signer en concaténant timestamp + "." + payload
  3. Décoder la clé de signature à partir du base64 (disponible dans les détails du webhook)
  4. Calculer le HMAC-SHA256 en utilisant la clé décodée et les données à signer
  5. Comparer le résultat avec la signature de l’en-tête
Exigences importantes :
  • Retirer 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 du base64 pour le calcul HMAC
  • S’assurer d’une correspondance exacte des chaînes pour que la vérification réussisse
Obtenir votre clé de signature :
  1. Accédez à la page des détails du webhook dans OpenPhone
  2. Cliquez sur les points de suspension (⋯) en haut à droite
  3. Sélectionnez « Afficher le secret de signature »
  4. Copiez 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 is from "Reveal Signing Secret" in the OpenPhone app.
  const signingKey = 'R2ZLM2o0bFhBNVpyUnU2NG9mYXQ1MHNyR3pvSUhIVVg='

  // Parse the fields from the openphone-signature header.
  const signature = req.headers['openphone-signature']
  const fields = signature.split(';')
  const timestamp = fields[2]
  const providedDigest = fields[3]

  // Compute the data covered by the signature.
  const signedData = timestamp + '.' + JSON.stringify(req.body)

  // Convert the base64-encoded signing key to binary.
  const signingKeyBinary = Buffer.from(signingKey, 'base64').toString('binary')

  // Compute the SHA256 HMAC digest.
  const computedDigest = crypto.createHmac('sha256',signingKeyBinary)
    .update(Buffer.from(signedData,'utf8'))
    .digest('base64')

  // Verify signature matches
  if (providedDigest === computedDigest) {
    console.log(`signature verification succeeded`)
    // Process webhook event here
  } else {
    console.log(`signature verification failed`)
    return res.status(401).send('Unauthorized')
  }

  res.send({})
});

app.listen(port, function () {
  console.log(`webhook server listening on 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 « Révéler le secret de signature » dans l'application OpenPhone.
    signing_key = 'R2ZLM2o0bFhBNVpyUnU2NG9mYXQ1MHNyR3pvSUhIVVg='

    # Analyser 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 pourraient inclure plusieurs signatures séparées par des virgules. Séparez la valeur de l’en-tête par des virgules pour gérer plusieurs signatures au besoin.

Meilleures 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 (p. 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 du débit pour les points de terminaison webhook

Gestion des erreurs et nouvelles tentatives

Système de nouvelles tentatives automatiques

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 reprise
  • Échec final : Notification par courriel envoyée au créateur du webhook
Chronologie des nouvelles tentatives :
  • Les nouvelles tentatives initiales se produisent rapidement pour minimiser les délais
  • Les délais augmentent de façon exponentielle à chaque tentative
  • Priorise la livraison près du moment de l’événement d’origine
  • Suivi automatique du statut tout au long du processus

Options de nouvelles tentatives manuelles

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 « échec »
  • Les nouvelles tentatives manuelles réussies mettent à jour le statut à « succès »

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 ayant échoué de façon permanente
  • Les détails de l’événement sont conservés pour révision manuelle
  • Option 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 contre localhost pendant le développement
  • Charges utiles simulées : Créez des exemples d’événements JSON 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. Naviguez vers la page de détails du webhook dans OpenPhone
  2. Cliquez sur les points de suspension (⋯) en haut à droite
  3. Sélectionnez « Envoyer une requête de test »
  4. OpenPhone envoie un événement d’exemple à votre URL de webhook
  5. Vérifiez la vérification 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 direct

Déclencheurs d’événements réels :
  • Configurez le webhook pour un type d’événement spécifique (p. ex., message.received)
  • Sélectionnez votre numéro OpenPhone dans les ressources du webhook
  • Déclenchez un événement réel (envoyez un texto à votre numéro)
  • Surveillez la livraison et le traitement du webhook
  • Vérifiez la fonctionnalité complète de bout en bout
Liste de vérification des tests :
  • Le webhook reçoit les requêtes POST correctement
  • La vérification de signature fonctionne
  • L’analyse de la charge utile d’événement réussit
  • La logique d’application 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 : Retournez les réponses dans le délai limite 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 reprise : Examinez les séquences de reprise automatiques
  • Tests manuels : Utilisez « Envoyer une demande 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 reprises
  • Journalisation : Implémentez une journalisation complète des demandes/réponses
  • Surveillance : Configurez des alertes pour les échecs de livraison de webhook

Exemples de données d’événement

Événements de message

Données 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"  
    }  
  }  
}
Données 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

Données 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 avec réponse) :
{  
  "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"  
    }  
  }  
}
Données 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

Contenu 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

Contenu 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."  
      ]  
    }  
  }  
}
Charge utile 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 de soutien à support.openphone.com pour obtenir de l’assistance technique avec l’implémentation de webhook.