ਹਰ ਕਾਲ ਲਈ ਡਾਇਨਾਮਿਕ ਕਨਫਿਗਰੇਸ਼ਨ

ਮੂਲ ਰੂਪ ਵਿੱਚ ਹਰ ਫੋਨ ਨੰਬਰ ਅਤੇ ਪਬਲਿਸ਼ ਕਰਨ ਯੋਗ ਕੁੰਜੀ ਨਾਲ ਇੱਕ ਸਥਿਰ ਏਜੰਟ ਨਿਰਧਾਰਤ ਹੁੰਦਾ ਹੈ। ਜਦੋਂ ਤੁਹਾਨੂੰ ਹਰੇਕ ਕਾਲਰ ਲਈ ਜਾਂ ਹਰੇਕ ਵਿਜ਼ਟਰ ਲਈ ਅਨੁਕੂਲਤਾ ਚਾਹੀਦੀ ਹੋਵੇ — VIP ਰੂਟਿੰਗ, ਲੌਗ-ਇਨ ਕੀਤੇ ਯੂਜ਼ਰ ਦਾ ਸੰਦਰਭ, A/B prompt ਟੈਸਟ — ਤਾਂ ਵੈੱਬਹੁੱਕ ਮੋਡ 'ਤੇ ਜਾਓ ਅਤੇ ਆਪਣੇ ਸਰਵਰ ਨੂੰ ਫ਼ੈਸਲਾ ਕਰਨ ਦਿਓ।

ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ

  1. ਤੁਸੀਂ telephony.incoming (ਫੋਨ) ਜਾਂ web.incoming (ਵਿਜਟ) ਇਵੈਂਟ ਨੂੰ ਸਬਸਕ੍ਰਾਈਬ ਕਰਦੇ ਹੋ। ਦੋਵੇਂ ਬਲੌਕਿੰਗ ਵੈੱਬਹੁੱਕ ਹਨ: ThunderPhone ਕਾਲ ਜਾਰੀ ਰੱਖਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਜਵਾਬ ਲਈ 10 ਸਕਿੰਟ ਤੱਕ ਉਡੀਕ ਕਰਦਾ ਹੈ।
  2. ThunderPhone ਤੁਹਾਨੂੰ {call_id, from_number, to_number} ਭੇਜਦਾ ਹੈ (ਵਿਜਟ ਸੈਸ਼ਨਾਂ ਵਿੱਚ ਨੰਬਰਾਂ ਦੀ ਥਾਂ ਵਿਜਟ-ਵਿਸ਼ੇਸ਼ ਫ਼ੀਲਡ ਹੁੰਦੇ ਹਨ — ਬੇਨਤੀ ਸਕੀਮਾ ਵੇਖੋ)।
  3. ਤੁਹਾਡਾ ਸਰਵਰ ਏਜੰਟ ਸੰਰਚਨਾ (prompt, ਆਵਾਜ਼, ਉਤਪਾਦ, ਟੂਲ) ਨਾਲ ਜਵਾਬ ਦਿੰਦਾ ਹੈ। ThunderPhone ਕਾਲ ਲਈ ਉਸ ਸੰਰਚਨਾ ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ।
  4. ਜੇ ਤੁਸੀਂ {} ਵਾਪਸ ਕਰਦੇ ਹੋ, ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਜਾਂਦਾ ਹੈ, ਜਾਂ ਗਲਤੀ ਆਉਂਦੀ ਹੈ, ਤਾਂ ਸਥਿਰ ਤੌਰ 'ਤੇ ਨਿਰਧਾਰਤ ਏਜੰਟ ਨੂੰ ਫਾਲਬੈਕ ਵਜੋਂ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ। ਸੁਰੱਖਿਅਤ ਡਿਫਾਲਟ।

1. ਵੈੱਬਹੁੱਕ ਟਿਕਾਣਾ ਸੰਰਚਿਤ ਕਰੋ

ਫੋਨ ਕਾਲਾਂ

ਫੋਨ ਨੰਬਰਾਂ ਲਈ, ਆਪਣੇ ਐਂਡਪੌਇੰਟ ਨੂੰ 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 ਸ਼ਾਮਲ ਹੁੰਦਾ ਹੈ — ਇਸਨੂੰ ਸੰਭਾਲੋ; ਤੁਸੀਂ ਇਸਦੀ ਵਰਤੋਂ ਦਸਤਖਤ ਤਸਦੀਕ ਲਈ ਕਰੋਗੇ।

ਵੈੱਬ ਵਿਜਟ

ਵਿਜਟ ਸੈਸ਼ਨਾਂ ਲਈ, ਆਪਣੇ ਐਂਡਪੌਇੰਟ URL ਨੂੰ ਸ਼ਾਮਲ ਕਰਕੇ mode="webhook" ਵਿੱਚ ਇੱਕ ਪਬਲਿਸ਼ ਕਰਨ ਯੋਗ ਕੁੰਜੀ ਬਣਾਓ:

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"]
  }'

ਵਿਜਟ ਹਰ ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਹੋਣ 'ਤੇ ਇਸ URL ਨੂੰ POST ਕਰੇਗਾ।

2. ਹੈਂਡਲਰ ਲਾਗੂ ਕਰੋ

ਤਿੰਨ ਆਮ ਨਿਯਮ:

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ਸਟ੍ਰਿੰਗ (ਲਾਜ਼ਮੀ)ਏਜੰਟ ਲਈ ਸਿਸਟਮ prompt
voiceਸਟ੍ਰਿੰਗ (ਲਾਜ਼ਮੀ)GET /v1/voices ਤੋਂ ਵੌਇਸ id
productਸਟ੍ਰਿੰਗਡਿਫੌਲਟ spark ਹੈ
background_trackਸਟ੍ਰਿੰਗ | nullਐਂਬੀਅੰਟ ਆਡੀਓ id
acknowledgement_prompt_modeਸਟ੍ਰਿੰਗauto ਜਾਂ manual (ਸਿਰਫ਼ acknowledgement ਵਾਲੇ Storm ਲਈ)
acknowledgement_promptਸਟ੍ਰਿੰਗਜਦੋਂ ਮੋਡ manual ਹੋਵੇ ਤਾਂ ਲਾਜ਼ਮੀ
toolsਐਰੇਇਨਲਾਈਨ ਫੰਕਸ਼ਨ-ਟੂਲ ਸਕੀਮੇ — ਫੰਕਸ਼ਨ ਟੂਲਜ਼ ਵੇਖੋ

ਪੈਟਰਨ

ਲੌਗ-ਇਨ ਕੀਤੇ ਯੂਜ਼ਰ ਦਾ ਸੰਦਰਭ

ਵੈੱਬਹੁੱਕ-ਮੋਡ ਵਿਜੇਟਾਂ ਵਿੱਚ, ਵਿਜ਼ਟਰ ਦੇ ਪੇਜ ਨੂੰ ਪਹਿਲਾਂ ਹੀ ਪਤਾ ਹੁੰਦਾ ਹੈ ਕਿ ਉਹ ਕੌਣ ਹਨ। ਆਪਣੇ ਵੈੱਬਹੁੱਕ ਨੂੰ ਇੱਕ ਕਵੇਰੀ ਸਟ੍ਰਿੰਗ ਪੈਰਾਮੀਟਰ ਨਾਲ ਕਾਲ ਕਰੋ ਜਿਸਨੂੰ ਵਿਜੇਟ SDK ਫਾਰਵਰਡ ਕਰਦਾ ਹੈ (?customer_id=123) ਅਤੇ ਸਰਵਰ-ਸਾਈਡ 'ਤੇ ਗਾਹਕ ਨੂੰ ਲੱਭੋ।

A/B prompt ਰੋਲਆਉਟ

ਇਸਨੂੰ ਖੁਦ ਬਣਾਉਣ ਤੋਂ ਪਹਿਲਾਂ, ਧਿਆਨ ਦਿਓ ਕਿ ThunderPhone ਵਿੱਚ ਇੱਕ ਨੈਟਿਵ ਪ੍ਰਯੋਗਾਂ ਦੀ ਵਿਸ਼ੇਸ਼ਤਾ (/dashboard/experiments ਅਤੇ ਏਜੰਟ ਬਿਲਡਰ ਦੀ A/B ਟੈਬ) ਹੈ ਜੋ ਵੈਰੀਐਂਟ ਪਰਿਭਾਸ਼ਿਤ ਕਰਦੀ ਹੈ, ਟ੍ਰੈਫਿਕ ਵੰਡਦੀ ਹੈ, ਅਤੇ ਹਰ ਵੈਰੀਐਂਟ ਦੇ ਨਤੀਜਿਆਂ ਦੀ ਤੁਲਨਾ ਕਰਦੀ ਹੈ — ਵੈੱਬਹੁੱਕ ਦੀ ਲੋੜ ਨਹੀਂ।

ਜੇ ਤੁਹਾਨੂੰ ਫਿਰ ਵੀ ਵੈੱਬਹੁੱਕ-ਸਾਈਡ ਕੰਟਰੋਲ ਦੀ ਲੋੜ ਹੈ: call_id ਨੂੰ ਹੈਸ਼ ਕਰੋ → ਬੱਕੇਟ; 0..49 ਲਈ prompt A ਅਤੇ 50..99 ਲਈ prompt B ਸਰਵ ਕਰੋ। ਆਪਣੇ DB ਵਿੱਚ ਇਹ ਰਿਕਾਰਡ ਕਰੋ ਕਿ ਤੁਸੀਂ ਕਿਹੜਾ ਬੱਕੇਟ ਚੁਣਿਆ ਸੀ ਅਤੇ ਬਾਅਦ ਵਿੱਚ ਪੂਰੀ ਹੋਈ ਕਾਲ ਦੇ ਗ੍ਰੇਡ ਨਾਲ ਇਸਦਾ ਸਬੰਧ ਜੋੜੋ।

ਸਮੇਂ-ਅਧਾਰਿਤ ਰੂਟਿੰਗ

ਕਾਰੋਬਾਰੀ ਸਮੇਂ → "ਲਾਈਵ ਸਹਾਇਤਾ" ਏਜੰਟ; ਕਾਰੋਬਾਰੀ ਸਮੇਂ ਤੋਂ ਬਾਅਦ → "ਸੁਨੇਹਾ ਲਓ" ਏਜੰਟ। ਆਪਣੇ ਹੈਂਡਲਰ ਵਿੱਚ new Date().getUTCHours() 'ਤੇ ਸਧਾਰਨ ਸਵਿੱਚ ਵਰਤੋ।


ਅਗਲੇ ਕਦਮ