ThunderPhone 2.0 متاح الآن.خدمة ذاتية، ابتداءً من 2 سنت/دقيقة.اقرأ الإعلان

Developer cookbook

التهيئة الديناميكية لكل مكالمة

اختر الوكيل الذي يجيب — أو أعد كتابة موجّهه وإعداداته — بشكل منفصل لكل مكالمة واردة، استنادًا إلى منطق مخصص في webhook تتحكم به.

افتراضيًا، يُسنَد وكيل ثابت إلى كل رقم هاتف ومفتاح قابل للنشر. عندما تحتاج إلى تخصيص لكل متصل أو لكل زائر — توجيه كبار الشخصيات، وسياق المستخدم المسجّل دخوله، واختبارات موجّه A/B — انتقل إلى وضع webhook ودع خادمك يقرر.

آلية العمل

  1. تشترك في حدث telephony.incoming (الهاتف) أو web.incoming (عنصر الواجهة). كلاهما خطافات ويب حاجبة: ينتظر ThunderPhone ردك قبل متابعة المكالمة، لمدة تصل إلى 13 ثانية لمكالمة هاتفية (بما في ذلك إعادة محاولة واحدة) و10 ثوانٍ لجلسة عنصر واجهة.
  2. يرسل إليك ThunderPhone {call_id, from_number, to_number} (تحمل جلسات عنصر الواجهة حقولًا خاصة بعنصر الواجهة بدلًا من الأرقام — راجع مخطط الطلب).
  3. يستجيب خادمك بإعدادات وكيل (موجّه، صوت، منتج، أدوات). يستخدم ThunderPhone تلك الإعدادات للمكالمة.
  4. لا يوجد وكيل احتياطي. إذا أرجعت {} أو إعدادات غير صالحة، أو استمر فشل الطلب بعد إعادة المحاولة، تُنهى المكالمة الهاتفية فورًا ويُسجَّل فيها end_reason inbound_config_webhook_failed؛ ويفشل طلب جلسة عنصر الواجهة.

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 للاستخدام مرة واحدة — احفظه؛ ستستخدمه للتحقق من التوقيع.

بالنسبة إلى جلسات الأداة المصغّرة، أنشئ مفتاحًا قابلًا للنشر في 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"]
  }'

سترسل الأداة المصغّرة طلب POST إلى عنوان URL هذا عند بدء كل جلسة.

2. نفّذ المعالج

ثلاث قواعد أساسية:

  • تحقق من التوقيع في كل طلب (راجع التحقق من توقيعات webhook). لا تتجاوز هذه الخطوة في بيئة التطوير — نفّذها بشكل صحيح مرة واحدة وأعِد استخدامها.
  • استجب بسرعة. تُقطع المحاولة الأولى للمكالمة الهاتفية بعد 10 ثوانٍ، ويُقطع التبادل بالكامل بعد 13 ثانية، وكل ثانية تعني صمتًا للمتصل. أجرِ عمليات البحث في قاعدة البيانات عند الحاجة، لكن لا تستدعِ نماذج اللغة الكبيرة اللاحقة بشكل متزامن — إذا أردت إنشاء موجّه ديناميكي، فاحسبه مسبقًا وخزّنه مؤقتًا.
  • ارجع إلى الإعداد الافتراضي بسلاسة. لأي حالة غير متوقعة، استجب باستخدام prompt وvoice افتراضيين (أو agent_id محفوظ). تؤدي إعادة {} أو حالة خطأ إلى إنهاء المكالمة.
FastAPI
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
Express
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 مع الإقرار فقط)
acknowledgement_promptسلسلة نصيةمطلوب عندما يكون الوضع manual
language_switchingسلسلة نصيةللوكلاء متعددي اللغات، ask_first أو follow_caller
toolsمصفوفةمخططات أدوات الدوال المضمنة — راجع أدوات الدوال

احتفظ بوكيل محفوظ وقدّم متغيرات

أعِد {"agent_id": 12, "variables": {"name": "Ada"}} لاستخدام الوكيل المحفوظ لتلك المؤسسة مع بيانات لكل مكالمة. يمكن أن يحتوي موجّهه على {{name}} أو {{name|Friend}}. تندمج متغيرات خطاف الويب فوق المتغيرات على مستوى الطلب؛ وتستخدم القيمة الفارغة القيمة الافتراضية للنائب، أو نصًا فارغًا إذا لم تُوفَّر قيمة افتراضية. تظهر القيم النهائية والأسماء غير المحلولة في تفاصيل المكالمة وخطافات ويب الإكمال. لا تقبل استجابات الوكيل المحفوظ سوى agent_id وvariables وtimezone. إذا كان prompt موجودًا، تستخدم الاستجابة تهيئة مضمّنة وتتجاهل agent_id (بما في ذلك البيانات الوصفية الفارغة أو غير الصحيحة العدد)؛ ويجب أن يظل الموجّه المضمّن صالحًا. قد تتضمن استجابات التهيئة المضمّنة أيضًا variables. تستخدم استجابات الوكيل المحفوظ تقسيم A/B المنشور للوكيل في كلٍّ من مكالمات الهاتف والأداة، ثم تعرض المتغيرات. راجع متغيرات المكالمة للتعرّف على الحدود ودعم واجهة برمجة تطبيقات الجلسة. تأتي تهيئة الحظر من عنوان URL القديم لرقم الهاتف/المؤسسة أو من مفتاح أداة في وضع خطاف الويب؛ أما أحداث الوارد لنظام نقطة النهاية فهي إشعارات فقط.

اضبط المنطقة الزمنية لكل مكالمة

يمكن لوكيل واحد الرد على جهات اتصال في مناطق زمنية مختلفة. أضف timezone، وهو اسم IANA، إلى استجابة وكيل محفوظ أو استجابة مضمنة، وسيذكر الوكيل التاريخ والوقت ويعرض أوقاتًا في تلك المنطقة لهذه المكالمة، قبل منطقته الزمنية الخاصة:

{"agent_id": 12, "timezone": "America/Chicago", "variables": {"name": "Ada"}}

أرسل المنطقة بدلًا من طابع زمني في variables: تستمر ساعة الوكيل في العمل أثناء المكالمة، ويصبح الوقت الذي تمرره قديمًا. تُتجاهل القيمة التي لا تمثل منطقة IANA، وتتصل المكالمة ضمن منطقة الوكيل.

الأنماط

سياق المستخدم الذي سجّل الدخول

في الأدوات المصغرة بوضع خطاف الويب، تعرف صفحة الزائر مسبقًا من يكون. استدعِ خطاف الويب الخاص بك باستخدام معلَمة سلسلة استعلام تمررها حزمة SDK الخاصة بالأداة المصغرة (?customer_id=123) وابحث عن العميل من جهة الخادم.

طرح موجّه A/B

قبل تنفيذ ذلك يدويًا، لاحظ أن ThunderPhone يوفر ميزة التجارب الأصلية (/dashboard/experiments وعلامة التبويب A/B في منشئ الوكلاء) التي تعرّف المتغيرات، وتقسم الزيارات، وتقارن النتائج لكل متغير — من دون الحاجة إلى خطاف ويب.

إذا كنت تحتاج إلى التحكم من جهة خطاف الويب رغم ذلك: اعمل تجزئة لـ call_id → مجموعة؛ قدّم الموجّه A للنطاق 0..49 والموجّه B للنطاق 50..99. سجّل المجموعة التي اخترتها في قاعدة البيانات الخاصة بك، واربطها لاحقًا بتقييم المكالمة المكتملة.

التوجيه حسب الوقت

ساعات العمل → وكيل «الدعم المباشر»؛ خارج ساعات العمل → وكيل «تسجيل رسالة». تبديل بسيط بناءً على new Date().getUTCHours() في معالجك.


الخطوات التالية