ThunderPhone 2.0 је стигао.Почните самостално, већ од 2 ¢/мин.Прочитајте објаву

Webhooks

Преглед вебхукова

Како ThunderPhone испоручује догађаје у реалном времену, како да проверите потписе и како се пореде застарели и модели испоруке засновани на крајњим тачкама.

ThunderPhone шаље HTTP POST захтеве Вашем серверу када се нешто догоди током позива — започне се долазни позив, позив се заврши, заврши се покретање оцењивања, активира се упозорење и тако даље. Постоје два модела испоруке:

Свих десет типова догађаја из каталога догађаја испоручује се преко крајњих тачака веб-кука. Шест догађаја животног циклуса позива (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) се такође шаљу на застарелу веб-куку са једним URL-ом — ако имате и застарели URL и одговарајућу крајњу тачку, догађај примате на обе путање. Блокирајуће понашање (размена конфигурације telephony.incoming / web.incoming и прослеђивање алатке у режиму веб-куке) постоји искључиво на застарелој путањи; свака испорука крајњој тачки је обавештење без чекања одговора.

Формат корисног терета

Испоруке крајњим тачкама су 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 је јединствен за сваки емитовани догађај. Исти је при поновним покушајима и на свакој крајњој тачки која прими догађај — користите га за уклањање дупликата.

Застарела веб-кука са једним 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 веб-куке на нивоу Ваше организације за застареле испоруке).

Кораци

  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 / ИД-а објекта.

Дупликати

Испорука је најмање једном: поновни покушај након одговора који никада нисмо примили може дуплирати догађај. Сваки поновни покушај носи исти event_id, зато сачувајте обрађене ИД-ове и прескочите понављања. event_id се такође дели између крајњих тачака — две крајње тачке претплаћене на исти догађај примају исти event_id.

Истек времена

Испоруке до крајњих тачака имају истек времена од 30 с по покушају. На наслеђеној путањи, блокирајући захтеви који управљају понашањем активног позива — размена конфигурације telephony.incoming / web.incoming — истичу након 10 с, али спор одговор одлаже јављање на позив, зато настојте да одговорите у року од неколико секунди. Испорука алатки у режиму вебхука дозвољава 20 с подразумевано, а декларације алатки могу поставити timeout највишег нивоа.

Изворне IP адресе

Одлазни вебхукови потичу из ThunderPhone опсега IP адреса у облаку. Ако ваш заштитни зид захтева листу дозвољених адреса, обратите се подршци и доставићемо тренутне опсеге.

Избор између наслеђених вебхукова и вебхукова заснованих на крајњим тачкама

ФункцијаНаслеђени (/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 само ако динамички конфигуришете позиве у тренутку јављања или користите испоруку алатки у режиму вебхука — те размене захтева и одговора извршавају се само на наслеђеној путањи.


Повезано