Επισκόπηση Webhook

Το ThunderPhone στέλνει αιτήματα HTTP POST στον διακομιστή σας όταν συμβαίνουν ενέργειες κατά τη διάρκεια μιας κλήσης — ξεκινά μια εισερχόμενη κλήση, ολοκληρώνεται μια κλήση, ολοκληρώνεται μια εκτέλεση αξιολόγησης, ενεργοποιείται μια ειδοποίηση κ.ο.κ. Υπάρχουν δύο μοντέλα παράδοσης:

Και οι δέκα τύποι συμβάντων στον κατάλογο συμβάντων παραδίδονται μέσω τελικών σημείων webhook. Τα έξι συμβάντα κύκλου ζωής κλήσης (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) αποστέλλονται επίσης στο webhook παλαιού τύπου με ένα URL — αν έχετε και ένα URL παλαιού τύπου και ένα αντίστοιχο τελικό σημείο, λαμβάνετε το συμβάν και από τις δύο διαδρομές. Η μπλοκαριστική συμπεριφορά (η ανταλλαγή ρυθμίσεων telephony.incoming / web.incoming και η δρομολόγηση εργαλείων σε λειτουργία webhook) υπάρχει αποκλειστικά στη διαδρομή παλαιού τύπου· κάθε παράδοση σε τελικό σημείο είναι ειδοποίηση χωρίς αναμονή.

Μορφή ωφέλιμου φορτίου

Οι παραδόσεις σε τελικά σημεία είναι ένα αντικείμενο JSON με data, event_id και type:

{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}

Το event_id είναι μοναδικό για κάθε εκπεμπόμενο συμβάν. Είναι ίδιο σε όλες τις επαναλήψεις και σε κάθε τελικό σημείο που λαμβάνει το συμβάν — κάντε αποδιπλοποίηση βάσει αυτού.

Το webhook παλαιού τύπου με ένα URL στέλνει τα ίδια type και data, αλλά χωρίς event_id:

{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}

Στο δίκτυο, κάθε σώμα σειριοποιείται κανονικά — τα κλειδιά ταξινομούνται αλφαβητικά, χωρίς κενά, σε UTF-8. Τα παραδείγματα με μορφοποίηση σε αυτές τις τεκμηριώσεις παρέχονται μόνο για αναγνωσιμότητα.

Δείτε τον Κατάλογο συμβάντων για την πλήρη λίστα τύπων συμβάντων και πεδίων ωφέλιμου φορτίου.

Επαλήθευση υπογραφής

Κάθε αίτημα περιλαμβάνει υπογραφή HMAC-SHA256 για το ακατέργαστο σώμα αιτήματος στην κεφαλίδα X-ThunderPhone-Signature. Το κλειδί υπογραφής είναι το secret του τελικού σημείου (ή το secret του webhook σε επίπεδο οργανισμού για παλαιότερες παραδόσεις).

Βήματα

  1. Διαβάστε το ακατέργαστο σώμα αιτήματος πριν από οποιαδήποτε ανάλυση.
  2. Υπολογίστε hmac_sha256(secret, body).hexdigest().
  3. Συγκρίνετε σε σταθερό χρόνο με την κεφαλίδα X-ThunderPhone-Signature.

Υπογράφουμε ακριβώς τα byte που μεταδίδουμε και αυτά τα byte είναι η κανονική σειριοποίηση JSON (ταξινομημένα κλειδιά, συμπαγείς διαχωριστές). Επομένως, η επαλήθευση με βάση το ακατέργαστο σώμα λειτουργεί πάντα — και αν το πλαίσιο εργασίας σας παρέχει μόνο αναλυμένο JSON, η επανασειριοποίησή του με ταξινομημένα κλειδιά και συμπαγείς διαχωριστές παράγει πανομοιότυπα byte. Και οι δύο μέθοδοι καλύπτονται στον οδηγό επαλήθευσης.

import hmac
import hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode("utf-8"),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature or "")

# Example Flask handler
from flask import Flask, request, abort
app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    body = request.get_data()
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify_signature(body, sig, WEBHOOK_SECRET):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
import crypto from "node:crypto";
import express from "express";

function verifySignature(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  if (!signature || expected.length !== signature.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.header("X-ThunderPhone-Signature") || "";
    if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);

Σημασιολογία παράδοσης

Αυτή η σημασιολογία εφαρμόζεται στις παραδόσεις σε σημεία τερματισμού. Το παλαιού τύπου webhook με ένα URL πραγματοποιεί μία μόνο σύγχρονη προσπάθεια χωρίς επαναλήψεις.

Επαναλήψεις

Κάθε συμβάν επιχειρείται μία φορά άμεσα. Κάθε απόκριση 2xx επιβεβαιώνει την παράδοση. Σε κάθε άλλη έκβαση (εκτός 2xx, σφάλμα σύνδεσης, χρονικό όριο) επαναλαμβάνουμε στις 1 λεπτό, 5 λεπτά, 30 λεπτά, 2 ώρες, 6 ώρες, 12 ώρες και 24 ώρες μετά την πρώτη προσπάθεια — 8 προσπάθειες σε διάστημα 24 ωρών. Αν αποτύχουν όλες οι προσπάθειες, η παράδοση διακόπτεται και το σημείο τερματισμού επισημαίνεται με status="failing" στα σημεία τερματισμού webhook. Επιστρέψτε 2xx μόλις το ωφέλιμο φορτίο γίνει δεκτό με μόνιμο τρόπο· επεξεργαστείτε το ασύγχρονα.

Σειρά

Η σειρά παράδοσης καταβάλλει τη βέλτιστη δυνατή προσπάθεια. Στην πράξη παραδίδουμε με τη σειρά με την οποία εκπέμπονται τα συμβάντα, αλλά οι επαναλήψεις μπορούν να αλλάξουν τη σειρά σε περίπτωση αποτυχίας. Να εξαλείφετε πάντα τα διπλότυπα και να συμφωνείτε τα δεδομένα βάσει call_id / αναγνωριστικού αντικειμένου.

Διπλότυπα

Η παράδοση είναι τουλάχιστον μία φορά: μια επανάληψη μετά από απόκριση που δεν λάβαμε ποτέ μπορεί να δημιουργήσει διπλότυπο συμβάν. Κάθε επανάληψη περιλαμβάνει το ίδιο event_id, επομένως αποθηκεύστε τα επεξεργασμένα αναγνωριστικά και παραλείψτε τις επαναλήψεις. Το event_id είναι επίσης κοινό μεταξύ σημείων τερματισμού — δύο σημεία τερματισμού που έχουν εγγραφεί στο ίδιο συμβάν λαμβάνουν το ίδιο event_id.

Χρονικά όρια

Οι παραδόσεις σε σημεία τερματισμού έχουν χρονικό όριο 30 δευτ. ανά προσπάθεια. Στη διαδρομή παλαιού τύπου, τα αιτήματα αποκλεισμού που καθοδηγούν τη συμπεριφορά ζωντανών κλήσεων — η ανταλλαγή ρυθμίσεων telephony.incoming / web.incoming — λήγουν μετά από 10 δευτ., αλλά μια αργή απόκριση καθυστερεί την απάντηση της κλήσης, επομένως επιδιώξτε να απαντάτε μέσα σε λίγα δευτερόλεπτα. Η αποστολή εργαλείων σε λειτουργία webhook επιτρέπει 20 δευτ.

IP προέλευσης

Τα εξερχόμενα webhook προέρχονται από το εύρος IP cloud του ThunderPhone. Αν το τείχος προστασίας σας απαιτεί λίστα επιτρεπόμενων διευθύνσεων, επικοινωνήστε με την υποστήριξη και θα μοιραστούμε τα τρέχοντα εύρη.

Επιλογή μεταξύ webhook παλαιού τύπου και webhook βάσει σημείων τερματισμού

ΔυνατότηταΠαλαιού τύπου (/v1/webhook)Σημεία τερματισμού (/v1/developer/webhook-endpoints)
Αριθμός URL1 ανά οργανισμόΠολλά ανά οργανισμό
Κάλυψη συμβάντωνΜόνο telephony.* / web.*Και οι 10 τύποι συμβάντων
Φίλτρο συμβάντωνΑνά σημείο τερματισμού
ΕπαναλήψειςΚαμία8 προσπάθειες σε 24 ώρες
Περιτύλιγμαtype + datatype + data + event_id
Περιστροφή μυστικούΑντικαθιστά το μοναδικό μυστικόΜυστικό ανά σημείο τερματισμού
Απενεργοποίηση χωρίς διαγραφήstatus=disabled
Ορατότητα κατάστασηςactive / disabled / failing
Ανταλλαγή ρυθμίσεων αποκλεισμούΝαι (telephony.incoming / web.incoming)Ποτέ — μόνο ειδοποιήσεις
Κατάλληλο γιαΔυναμική ρύθμιση κλήσεωνΚατανάλωση συμβάντων στην παραγωγή

Οι νέες ενσωματώσεις θα πρέπει να καταναλώνουν συμβάντα μέσω webhook βάσει σημείων τερματισμού. Διατηρήστε (ή προσθέστε) ένα URL παλαιού τύπου μόνο αν ρυθμίζετε κλήσεις δυναμικά κατά τη στιγμή της απάντησης ή χρησιμοποιείτε αποστολή εργαλείων σε λειτουργία webhook — αυτές οι ανταλλαγές αιτήματος/απόκρισης εκτελούνται μόνο στη διαδρομή παλαιού τύπου.


Σχετικά