Δυναμική ρύθμιση παραμέτρων ανά κλήση
Από προεπιλογή, σε κάθε αριθμό τηλεφώνου και δημοσιεύσιμο κλειδί έχει εκχωρηθεί ένας στατικός πράκτορας. Όταν χρειάζεστε προσαρμογή ανά καλούντα ή ανά επισκέπτη — δρομολόγηση VIP, περιβάλλον συνδεδεμένου χρήστη, δοκιμές προτροπών A/B — μεταβείτε σε λειτουργία webhook και αφήστε τον διακομιστή σας να αποφασίσει.
Πώς λειτουργεί
- Εγγράφετε συνδρομή στο συμβάν
telephony.incoming(τηλέφωνο) ήweb.incoming(widget). Και τα δύο είναι δεσμευτικά webhooks: το ThunderPhone περιμένει έως 10 δευτερόλεπτα για την απάντησή σας πριν συνεχίσει την κλήση. - Το ThunderPhone σάς στέλνει
{call_id, from_number, to_number}(οι συνεδρίες widget περιλαμβάνουν πεδία ειδικά για widget αντί για αριθμούς — δείτε το σχήμα αιτήματος). - Ο διακομιστής σας απαντά με μια διαμόρφωση πράκτορα (προτροπή, φωνή, προϊόν, εργαλεία). Το ThunderPhone χρησιμοποιεί αυτή τη διαμόρφωση για την κλήση.
- Αν επιστρέψετε
{}, προκύψει χρονικό όριο ή σφάλμα, χρησιμοποιείται ως εναλλακτική ο στατικά εκχωρημένος πράκτορας. Ασφαλής προεπιλογή.
1. Διαμορφώστε τον προορισμό webhook
Τηλεφωνικές κλήσεις
Για αριθμούς τηλεφώνου, εγγράψτε το τελικό σημείο σας στο telephony.incoming:
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Prod call-incoming",
"url": "https://example.com/thunderphone/incoming",
"events": ["telephony.incoming"]
}'
Η απάντηση περιλαμβάνει ένα εφάπαξ secret — αποθηκεύστε το· θα το χρησιμοποιήσετε
για επαλήθευση υπογραφής.
Widget ιστού
Για συνεδρίες widget, δημιουργήστε ένα δημοσιεύσιμο κλειδί σε mode="webhook"
με ενσωματωμένη τη διεύθυνση URL του τελικού σημείου σας:
curl -X POST https://api.thunderphone.com/v1/publishable-key \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Dynamic widget",
"mode": "webhook",
"webhook_url": "https://example.com/thunderphone/widget-incoming",
"allowed_domains": ["example.com"]
}'
Το widget θα στείλει POST σε αυτή τη διεύθυνση URL σε κάθε έναρξη συνεδρίας.
2. Υλοποιήστε τον χειριστή
Τρεις βασικοί κανόνες:
- Επαληθεύετε την υπογραφή σε κάθε αίτημα (δείτε Επαλήθευση υπογραφών webhook). Μην το παραλείπετε στο περιβάλλον ανάπτυξης — ρυθμίστε το σωστά μία φορά και επαναχρησιμοποιήστε το.
- Απαντάτε γρήγορα. Τα δέκα δευτερόλεπτα είναι το αυστηρό όριο και κάθε δευτερόλεπτο είναι νεκρός χρόνος για τον καλούντα. Κάντε αναζητήσεις στη βάση δεδομένων αν χρειάζεται, αλλά μην καλείτε κατάντη LLM συγχρονισμένα — αν θέλετε δυναμική δημιουργία προτροπών, προϋπολογίστε και αποθηκεύστε στην προσωρινή μνήμη.
- Χρησιμοποιείτε καθαρή εναλλακτική λύση. Κάθε μη αναμενόμενη κατάσταση πρέπει να επιστρέφει
{}, ώστε ο στατικά εκχωρημένος πράκτορας να διαχειρίζεται την κλήση.
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]
def verify(body: bytes, sig: str) -> bool:
expected = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig or "")
@app.post("/thunderphone/incoming")
async def incoming(request: Request):
body = await request.body()
if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
raise HTTPException(401)
event = json.loads(body)
if event["type"] not in ("telephony.incoming", "web.incoming"):
return {} # fall back to default
caller = event["data"]["from_number"]
# Cheap DB lookup: is this a known VIP?
customer = lookup_customer(caller)
if customer and customer.tier == "vip":
return {
"prompt": f"You are a VIP concierge for {customer.name}. Be proactive…",
"voice": "john",
"product": "storm-base",
}
return {} # default agent handles non-VIPs
def lookup_customer(phone: str):
# ... your CRM integration ...
pass
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;
function verify(body, sig) {
const expected = crypto.createHmac("sha256", SECRET).update(body).digest("hex");
return sig &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}
app.post(
"/thunderphone/incoming",
express.raw({ type: "application/json" }),
async (req, res) => {
if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
const IMPORTANT_TYPES = new Set([
"telephony.incoming",
"web.incoming",
]);
if (!IMPORTANT_TYPES.has(event.type)) return res.json({});
const customer = await lookupCustomer(event.data.from_number);
if (customer?.tier === "vip") {
return res.json({
prompt: `You are a VIP concierge for ${customer.name}. Be proactive…`,
voice: "john",
product: "storm-base",
});
}
res.json({}); // fall back to default agent
},
);
3. Σχήμα απόκρισης
Το σώμα της απόκρισης αντιστοιχεί ακριβώς στο σχήμα απόκρισης εισερχόμενης κλήσης. Τα πεδία που χρησιμοποιούνται συχνότερα:
| Πεδίο | Τύπος | Περιγραφή |
|---|---|---|
prompt | συμβολοσειρά (απαιτείται) | Προτροπή συστήματος για τον πράκτορα |
voice | συμβολοσειρά (απαιτείται) | Αναγνωριστικό φωνής από GET /v1/voices |
product | συμβολοσειρά | Η προεπιλογή είναι spark |
background_track | συμβολοσειρά | null | Αναγνωριστικό ήχου περιβάλλοντος |
acknowledgement_prompt_mode | συμβολοσειρά | auto ή manual (μόνο Storm-with-ack) |
acknowledgement_prompt | συμβολοσειρά | Απαιτείται όταν η λειτουργία είναι manual |
tools | πίνακας | Ενσωματωμένα σχήματα εργαλείων συναρτήσεων — δείτε Εργαλεία συναρτήσεων |
Μοτίβα
Περιβάλλον συνδεδεμένου χρήστη
Στα widget σε λειτουργία webhook, η σελίδα του επισκέπτη γνωρίζει ήδη ποιος
είναι. Καλέστε το webhook σας με μια παράμετρο συμβολοσειράς ερωτήματος που
προωθεί το SDK του widget (?customer_id=123) και αναζητήστε τον πελάτη στην πλευρά του διακομιστή.
Σταδιακή διάθεση προτροπής A/B
Πριν το υλοποιήσετε χειροκίνητα, σημειώστε ότι το ThunderPhone διαθέτει εγγενή
λειτουργία Πειραμάτων
(/dashboard/experiments και την καρτέλα A/B του εργαλείου δημιουργίας πρακτόρων) που
ορίζει παραλλαγές, κατανέμει την κίνηση και συγκρίνει αποτελέσματα ανά παραλλαγή —
δεν απαιτείται webhook.
Αν χρειάζεστε οπωσδήποτε έλεγχο από την πλευρά του webhook: κατακερματίστε το call_id → κάδος·
εξυπηρετήστε την προτροπή A για 0..49 και την προτροπή B για 50..99. Καταγράψτε τον
κάδο που επιλέξατε στη δική σας βάση δεδομένων και αργότερα συσχετίστε τον με την
αξιολόγηση της ολοκληρωμένης κλήσης.
Δρομολόγηση βάσει χρόνου
Ώρες λειτουργίας → πράκτορας «ζωντανής υποστήριξης»· εκτός ωραρίου → πράκτορας «λήψης μηνύματος».
Απλή εναλλαγή με βάση το new Date().getUTCHours() στον χειριστή σας.
Επόμενα βήματα
Ακριβή σχήματα αιτήματος και απόκρισης, συμπεριλαμβανομένου κάθε κλειδιού διαμόρφωσης.
Ρυθμίστε σωστά το HMAC μία φορά· επαναχρησιμοποιήστε το παντού.
Συνδυάστε δυναμική δρομολόγηση με εργαλεία ανά πράκτορα.
Επαναλήψεις, σειρά, χρονικά όρια.