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.

Fonctionnement des webhooks

Notifications pilotées par des événements : Les webhooks envoient des notifications instantanées lorsque des événements précis se produisent dans votre espace de travail OpenPhone. Lorsqu’un événement est déclenché, OpenPhone envoie une requête HTTP POST à l’URL que vous avez définie, avec des données détaillées sur 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. Lorsqu’un événement se produit, OpenPhone envoie une requête POST avec la charge utile de l’événement
  4. Votre application traite les données de l’événement et répond
  5. OpenPhone consigne l’état de la livraison et réessaie au besoin
La configuration des webhooks nécessite les autorisations de Propriétaire ou d’Admin de l’espace de travail. Les paramètres se gèrent uniquement dans les applications Web et de bureau.
Interface de configuration des webhooks :
Formulaire de création de webhook dans les paramètres d’OpenPhone

Événements Webhooks 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 distribué avec succès (inclut les éléments multimédias)
Analyses propulsées par l’IA :
  • call.summary.completed : Résumé d’appel généré par l’IA disponible dans la charge utile de l’événement
  • call.transcript.completed : Transcription complète de l’appel disponible dans la charge utile 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, peut inclure un message vocal)
  • call.recording.completed : Enregistrement d’appel disponible à l’URL fournie

Événements de contacts

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 des numéros de téléphone (appels/messages) ou des utilisateurs/groupes (contacts) à surveiller
Paramètres optionnels :
ParamètreDescription
LibelléNom descriptif pour identifier et gérer le webhook

Processus de configuration

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

Créer des gestionnaires de webhook

Exigences relatives au gestionnaire

Spécifications techniques :
  • Accepter les requêtes HTTP POST à votre URL de webhook
  • Traiter la charge utile (payload) d’événement JSON dans le corps de la requête
  • Répondre avec un code d’état HTTP 2xx en moins de 10 secondes
  • Vérifier la signature du webhook pour des raisons de sécurité
  • Gérer les nouvelles tentatives et les échecs de manière robuste
Gestion des réponses :
  • Succès : Retourner un code d’état 2xx (aucun corps de réponse requis)
  • Échec : Une réponse non 2xx déclenche la séquence de nouvelles tentatives d’OpenPhone
  • Délai d’attente : Aucune réponse en 10 secondes lance des tentatives de réessai
Souplesse de développement : Les gestionnaires de webhook peuvent être créé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 webhook incluent des signatures cryptographiques pour vérifier l’authenticité et prévenir les attaques par 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 Base64Digest SHA256

Processus de vérification de la 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 timestamp + "." + payload
  3. Décoder la clé de signature depuis Base64 (disponible dans les détails du webhook)
  4. Calculer l’HMAC-SHA256 à l’aide de la clé décodée et des 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 Base64 pour le calcul HMAC
  • Veiller à une correspondance exacte des chaînes pour réussir la vérification
Obtenir votre clé de signature :
  1. Accédez à la page des détails du webhook dans OpenPhone
  2. Cliquez sur l’ellipse (⋯) en haut à droite
  3. Sélectionnez « Reveal signing secret »
  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 « Reveal Signing Secret » 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 en 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 SHA-256.
    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 la signature réussie')
        # Traiter l’événement webhook ici
    else:
        print('échec de la vérification de la signature')
        return jsonify({'error': 'Unauthorized'}), 401
        
    return jsonify({})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)
De futures versions peuvent inclure plusieurs signatures séparées par des virgules. Scindez la valeur de l’en-tête par les virgules pour gérer plusieurs signatures au besoin.

Bonnes pratiques de sécurité

Protection contre les attaques par rejeu : Mettez en place une validation de l’horodatage pour prévenir les attaques par rejeu :
  • Comparez l’horodatage de la signature à l’heure actuelle
  • Rejetez les requêtes dont l’horodatage dépasse 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 façon sécuritaire (variables d’environnement, gestion des secrets)
  • Mettez en place une gestion appropriée des erreurs et de la journalisation
  • Envisagez la limitation du débit pour les points de terminaison des webhooks

Gestion des erreurs et réessais

Système de réessais automatique

Comportement des réessais :
  • Conditions de déclenchement : Codes de réponse non 2xx ou délai d’attente de 10 secondes
  • Stratégie de temporisation : Recul exponentiel avec délais croissants
  • Durée des réessais : Jusqu’à 3 jours de tentatives
  • Échec final : Notification par courriel envoyée au créateur du webhook
Calendrier des réessais :
  • Les premiers réessais surviennent rapidement pour minimiser le délai
  • Les délais augmentent de façon exponentielle à chaque tentative
  • Priorité donnée à une livraison près du moment de l’événement initial
  • Suivi de l’état automatique tout au long du processus

Options de réessais manuels

Gestion des webhooks :
  • Consulter l’état de livraison dans les détails du webhook OpenPhone
  • Relancer manuellement les appels de webhook échoués en tout temps
  • Les webhooks échoués sont marqués avec l’état « échec »
  • Les relances manuelles réussies mettent l’état à « succès »

Notifications d’échec

Lorsque les réessais sont épuisés :
  • Alerte par courriel envoyée au créateur du webhook
  • Appel de webhook marqué comme définitivement échoué
  • Détails de l’événement conservés pour examen manuel
  • Option de relancer manuellement une fois les problèmes résolus

Tests et validation

Tests en développement

Options de tests locaux :
  • Clients HTTP : utilisez cURL, Postman ou Insomnia pour envoyer des requêtes POST de test
  • URL locales : testez avec localhost pendant le développement
  • Charges utiles factices : créez des exemples d’événements JSON correspondant au format OpenPhone
  • Test 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 des 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 à l’URL de votre webhook
  5. Vérifiez la validation de la signature et la gestion de la réponse
Les requêtes de test nécessitent des URL de webhook accessibles publiquement. Les URL locales de développement ne fonctionnent 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 précis (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 remise et le traitement du webhook
  • Vérifiez la fonctionnalité de bout en bout
Liste de vérification des tests :
  • [ ] Le webhook reçoit correctement les requêtes POST
  • [ ] La vérification de la signature fonctionne
  • [ ] L’analyse de la charge utile de l’événement réussit
  • [ ] La logique de l’application traite correctement les événements
  • [ ] Les réponses d’erreur déclenchent des tentatives de renvoi
  • [ ] Les réponses de réussite arrêtent la séquence de renvois

Dépannage

Problèmes courants et solutions

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

Outils de débogage

Surveillance des événements :
  • Journal des événements : Affichez l’historique de livraison sur la page des détails du webhook
  • Suivi de l’état : Surveillez les taux de réussite et d’échec
  • Tentatives de nouvelle livraison : Examinez les séquences de réessais 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 réessais
  • Journalisation : Mettez en place une journalisation complète des requêtes et des réponses
  • Surveillance : Configurez des alertes pour les échecs de livraison des webhooks

Exemples de charges utiles d’événements

Événements de messages

Charge utile 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"  
    }  
  }  
}
Charge utile 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’appels

Charge utile 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 (appel 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"  
    }  
  }  
}
Charge utile 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 contacts

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": [  
        "Voici le résumé de votre appel."  
      ],  
      "nextSteps": [  
        "Voici vos prochaines étapes."  
      ]  
    }  
  }  
}
call.transcript.completed payload :
{  
  "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": "allo, 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.