Το ThunderPhone 2.0 είναι εδώ.Ξεκινήστε μόνοι σας, από 2¢/λεπτό.Διαβάστε την ανακοίνωση

Webhooks

Επισκόπηση Webhooks

Πώς το ThunderPhone παραδίδει συμβάντα σε πραγματικό χρόνο, πώς να επαληθεύετε υπογραφές και πώς συγκρίνονται τα παλαιότερα και τα βασισμένα σε τελικά σημεία μοντέλα παράδοσης.

Το 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 του endpoint (ή το secret του webhook σε επίπεδο οργανισμού για παλαιότερες παραδόσεις).

Βήματα

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

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

Python
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
Node.js (Express)
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 δ. από προεπιλογή και οι δηλώσεις εργαλείων μπορούν να ορίσουν timeout ανώτατου επιπέδου.

IP προέλευσης

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

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

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

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


Σχετικά