ThunderPhone 2.0 вече е тук.Започнете самостоятелно — от 2 цента/мин.Прочетете съобщението

Webhooks

Общ преглед на уебкуките

Как ThunderPhone доставя събития в реално време, как да проверявате подписи и как се сравняват наследеният и базираният на крайни точки модели за доставка.

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

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

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

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

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

Стъпки

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

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

Python
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
Node.js (Express)
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 сек. по подразбиране, а декларациите на инструменти могат да задават timeout от най-горно ниво.

Изходящи IP адреси

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

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

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

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


Свързани