telephony.incoming / web.incoming

Όταν μια εισερχόμενη τηλεφωνική κλήση φτάνει σε έναν αριθμό χωρίς εκχωρημένο πράκτορα, ή όταν ξεκινά μια συνεδρία widget ιστού με ένα δημοσιεύσιμο κλειδί σε mode="webhook", το ThunderPhone στέλνει ένα δεσμευτικό αίτημα telephony.incoming / web.incoming στο παλαιό webhook URL σας και περιμένει έως 10 δευτερόλεπτα για μια απάντηση διαμόρφωσης. Χρησιμοποιήστε αυτή την ανταλλαγή για να επιλέγετε δυναμικά prompt, φωνή και εργαλεία ανά κλήση — δείτε τον οδηγό δυναμικής διαμόρφωσης κλήσεων για την πλήρη ροή.

Η δεσμευτική ανταλλαγή δεν έχει εναλλακτική: αν ο χειριστής σας επιστρέψει κατάσταση εκτός 2xx, λήξει χρονικά ή επιστρέψει διαμόρφωση που αποτυγχάνει στην επικύρωση, η κλήση απορρίπτεται (η τηλεφωνική κλήση δεν συνδέεται· το αίτημα συνεδρίας widget αποτυγχάνει με 502/422). Απαντήστε γρήγορα — ο καλών ακούει ήχο κλήσης όσο αποφασίζετε.

Ωφέλιμο φορτίο αιτήματος

Για τηλεφωνικές κλήσεις (telephony.incoming):

{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
ΠεδίοΤύποςΠεριγραφή
call_idintegerΑναγνωριστικό κλήσης — σταθερό σε όλα τα συμβάντα αυτής της κλήσης
from_numberstringΑριθμός καλούντος E.164
to_numberstringΠροορισμός E.164 (ένας από τους αριθμούς ThunderPhone σας)

Για συνεδρίες widget ιστού (web.incoming), το data αναγνωρίζει τη σελίδα ενσωμάτωσης αντί για τηλεφωνικούς αριθμούς:

{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
ΠεδίοΤύποςΠεριγραφή
call_idintegerΑναγνωριστικό κλήσης
origin_domainstringΗ προέλευση της σελίδας που φιλοξενεί το widget
publishable_key_prefixstringΟι πρώτοι χαρακτήρες του δημοσιεύσιμου κλειδιού που άνοιξε τη συνεδρία
language, primary_languagestringΠαρόν όταν η συνεδρία widget ζήτησε παράκαμψη γλώσσας
voicestringΠαρόν όταν η συνεδρία widget ζήτησε παράκαμψη φωνής
website_contextstringΠαρόν όταν το widget πέρασε περιβάλλον σελίδας ανά συνεδρία

Σχήμα απόκρισης

Επιστρέψτε ένα αντικείμενο JSON που περιγράφει τη διαμόρφωση του πράκτορα για αυτή την κλήση. Τα prompt και voice είναι υποχρεωτικά· όλα τα υπόλοιπα είναι προαιρετικά.

{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
ΠεδίοΤύποςΥποχρεωτικόΠεριγραφή
promptstringναιΠροτροπή συστήματος που καθοδηγεί τον πράκτορα
voicestringναιΑναγνωριστικό φωνής από GET /v1/voices, π.χ. john. Το voice_name γίνεται αποδεκτό ως ψευδώνυμο. Άγνωστες φωνές αποτυγχάνουν στην επικύρωση και απορρίπτουν την κλήση
productstringόχιΠροεπιλογή: spark. Επιτρέπονται: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack
thinking_levelstringόχιminimal, base (προεπιλογή) ή extra. Αντικαθίσταται για προϊόντα Storm: το storm-extra* επιβάλλει extra, άλλα storm-* επιβάλλουν base
audio_context_modestringόχιfull (προεπιλογή) ή reduced
watchdog_enabledbooleanόχιΕνεργοποίηση εποπτείας για αυτή την κλήση. Προεπιλογή: false
storm_feedback_modestringόχιnone, acknowledgement (προεπιλογή) ή tick
languagestringόχιΣυντομογραφία για το primary_language
primary_languagestringόχιΚωδικός γλώσσας, κανονικοποιημένος (προεπιλογή en). Κωδικοί που δεν μπορούν να επιλυθούν απορρίπτουν την κλήση
has_additional_languagesbooleanόχιΠροεπιλογή: false
additional_languagesarray of stringόχιΕπιπλέον γλώσσες στις οποίες μπορεί να μεταβεί ο πράκτορας
background_trackstring | nullόχιΑναγνωριστικό ατμοσφαιρικού ήχου ή null
acknowledgement_prompt_modestringόχιauto (προεπιλογή) ή manual (προϊόντα Storm-with-ack)
acknowledgement_promptstringόχιΧρησιμοποιείται όταν acknowledgement_prompt_mode="manual"
silence_interval_secondsinteger | nullόχι5–120. Δευτερόλεπτα σιωπής του καλούντος πριν από έναν έλεγχο
silence_max_checkinsinteger | nullόχι1–10
silence_checkins_enabledbooleanόχιΠροεπιλογή: true
connect_tone_enabledbooleanόχιΠροεπιλογή: false
voicemail_actionstringόχιprompt (προεπιλογή), hangup ή message
voicemail_messagestringόχιΧρησιμοποιείται όταν voicemail_action="message"
agent_namestringόχιΕμφανιζόμενο όνομα που αναφέρεται στους πίνακες ελέγχου και στο γραφικό στοιχείο
org_namestringόχιΕμφανιζόμενο όνομα οργανισμού για την περσόνα του πράκτορα
toolsarrayόχιΕνσωματωμένα σχήματα εργαλείων συναρτήσεων (δείτε Εργαλεία συναρτήσεων)
call_idintegerόχιΠροαιρετική επανάληψη του αναγνωριστικού κλήσης του αιτήματος· αγνοείται

Επειδή τα prompt και voice είναι υποχρεωτικά, η επιστροφή {} ή οποιασδήποτε απόκρισης που αποτυγχάνει στην επικύρωση απορρίπτει την κλήση με 422 — δεν υπάρχει εφεδρικός στατικός πράκτορας σε αυτή τη διαδρομή (ένας αριθμός ή κλειδί σε λειτουργία webhook δεν έχει εκχωρημένο πράκτορα).


Όριο μεγέθους απόκρισης


Παράδειγμα χειριστή

import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]

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

@app.post("/thunderphone-webhook")
async def webhook(request: Request):
    body = await request.body()
    if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
        raise HTTPException(status_code=401)

    event = json.loads(body)
    if event["type"] == "telephony.incoming":
        caller = event["data"]["from_number"]
        prompt = (
            "Greet the caller as a San Francisco local…"
            if caller.startswith("+1415")
            else "You are a friendly customer support agent…"
        )
        return {
            "prompt": prompt,
            "voice": "john",
            "product": "spark",
        }
    if event["type"] == "web.incoming":
        return {
            "prompt": "You are the website's helpful voice assistant…",
            "voice": "john",
            "product": "spark",
        }
    return {}
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;

function verify(body, signature) {
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(body)
    .digest("hex");
  return signature &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));

    if (event.type === "telephony.incoming" || event.type === "web.incoming") {
      const caller = event.data.from_number || "web";
      const prompt = caller.startsWith("+1415")
        ? "Greet the caller as a San Francisco local…"
        : "You are a friendly customer support agent…";
      return res.json({
        prompt,
        voice: "john",
        product: "spark",
      });
    }
    res.json({});
  },
);

Απόκριση με εργαλεία συναρτήσεων

Επισυνάψτε εργαλεία ώστε ο AI πράκτορας να μπορεί να καλεί τα API σας κατά τη διάρκεια της συνομιλίας:

{
  "prompt":  "You are a booking assistant. Use the available tools to help customers schedule appointments.",
  "voice":   "john",
  "product": "spark",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": {
          "X-Api-Key": "your-key"
        }
      }
    }
  ]
}

Σύντομος οδηγός επιπέδων προϊόντος

ΠροϊόνΚαθυστέρησηΣυλλογισμόςΕπιβεβαίωση
sparkΗ χαμηλότερηΒασικός
boltΧαμηλήΒελτιωμένος
storm-baseΜέτριαΙσχυρός
storm-base-with-ackΜέτριαΙσχυρόςΑυτόματη συμπλήρωση κατά τη σκέψη
storm-extraΥψηλότερηΒαθύς
storm-extra-with-ackΥψηλότερηΒαθύςΑυτόματη συμπλήρωση κατά τη σκέψη

Σχετικά