Общ преглед на уебхуковете

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

При предаването всеки body се сериализира канонично — ключовете са сортирани по азбучен ред, без празни знаци, UTF-8. Примерите с форматиране в тази документация са само за по-лесно четене.

Вижте Каталога на събитията за пълния списък с типове събития и полета на полезния товар.

Проверка на подписа

Всяка заявка съдържа HMAC-SHA256 подпис върху необработеното тяло на заявката в заглавката X-ThunderPhone-Signature. Ключът за подписване е secret на крайната точка (или secret на webhook на ниво организация за наследени доставки).

Стъпки

  1. Прочетете необработеното тяло на заявката преди какъвто и да е анализ.
  2. Изчислете hmac_sha256(secret, body).hexdigest().
  3. Сравнете в константно време със заглавката X-ThunderPhone-Signature.

Подписваме точно байтовете, които изпращаме, а те представляват каноничната JSON сериализация (сортирани ключове, компактни разделители). Затова проверката спрямо необработеното тяло винаги работи — а ако вашата рамка ви предоставя само анализиран JSON, повторната му сериализация със сортирани ключове и компактни разделители създава идентични байтове. И двата подхода са описани в ръководството за проверка.

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
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);
  },
);

Семантика на доставката

Тази семантика се прилага за доставки до крайни точки. Наследената уебкука с един URL е единичен синхронен опит без повторни опити.

Повторни опити

Всяко събитие се опитва веднъж незабавно. Всеки отговор 2xx потвърждава доставката. При всеки друг резултат (не-2xx, грешка при свързване, изчакване) повтаряме опита 1 мин, 5 мин, 30 мин, 2 ч., 6 ч., 12 ч. и 24 ч. след първия опит — 8 опита в рамките на 24 часа. Ако всички опити са неуспешни, доставката спира и крайната точка се маркира с status="failing" в крайните точки за уебкуки. Върнете 2xx веднага щом полезният товар бъде трайно приет; обработвайте асинхронно.

Подреждане

Подреждането на доставките е с максимално усилие. На практика доставяме в реда, в който се генерират събитията, но повторните опити могат да променят реда при неуспех. Винаги премахвайте дубликатите и съгласувайте по call_id / id на обекта.

Дубликати

Доставката е поне веднъж: повторен опит след отговор, който не сме получили, може да дублира събитие. Всеки повторен опит носи същия event_id, затова съхранявайте обработените id и пропускайте повторенията. event_id се споделя и между крайните точки — две крайни точки, абонирани за едно и също събитие, получават един и същ event_id.

Изчаквания

Доставките до крайни точки имат изчакване от 30 сек за всеки опит. При наследения път блокиращите заявки, които управляват поведението на разговорите на живо — обменът за конфигуриране telephony.incoming / web.incoming — изтичат след 10 сек, но бавният отговор забавя приемането на обаждането, затова се стремете да отговорите в рамките на няколко секунди. Извикването на инструменти в режим на уебкуки позволява 20 сек.

IP адреси на източника

Изходящите уебкуки произхождат от диапазона облачни IP адреси на ThunderPhone. Ако защитната ви стена изисква списък с разрешени адреси, свържете се с поддръжката и ще споделим актуалните диапазони.

Избор между наследени уебкуки и уебкуки, базирани на крайни точки

ФункцияНаследени (/v1/webhook)Крайни точки (/v1/developer/webhook-endpoints)
Брой URL адреси1 на организацияМного на организация
Обхват на събитиятаСамо telephony.* / web.*Всички 10 типа събития
Филтър за събитияЗа всяка крайна точка
Повторни опитиНяма8 опита за 24 ч.
Обвивкаtype + datatype + data + event_id
Ротация на тайниЗаменя единичната тайнаТайна за всяка крайна точка
Деактивиране без изтриванеstatus=disabled
Видимост на състояниетоactive / disabled / failing
Блокиращ обмен за конфигуриранеДа (telephony.incoming / web.incoming)Никога — само известия
Най-подходящо заДинамична конфигурация на разговориКонсумиране на събития в продукционна среда

Новите интеграции трябва да консумират събития чрез уебкуки, базирани на крайни точки. Запазете (или добавете) наследен URL само ако конфигурирате разговори динамично в момента на приемане или използвате извикване на инструменти в режим на уебкуки — тези обмени заявка/отговор се изпълняват само по наследения път.


Свързани