Разработка и API 6 мин чтения

Webhooks - автоматическая доставка контента

Настройка Webhook, формат запроса, проверка HMAC-подписи. Примеры на Python, Node.js и PHP

Webhook позволяет автоматически получать сгенерированный контент на ваш сервер сразу после генерации. Taipy отправит POST-запрос с текстом и медиа-вложениями.

Как подключить

1

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

Откройте нужный проект и перейдите в его настройки.

2

Добавьте канал публикации

В разделе «Каналы публикации» нажмите «Добавить канал» и выберите платформу «Custom API / Webhook».

3

Укажите URL и секрет

В поле «ID канала/чата» вставьте ваш URL-эндпоинт (например: https://example.com/webhook). В поле «Токен бота» вставьте ваш HMAC-секрет для подписи запросов.

4

Выберите канал в кампании

В настройках кампании выберите этот канал как канал публикации.

Формат POST-запроса

При каждом событии Taipy отправляет JSON:

{{
  "event": "post.published",
  "timestamp": "2026-04-05T12:00:00+05:00",
  "data": {{
    "task_id": 123,
    "campaign_id": 456,
    "campaign_name": "Мой блог",
    "text": "Сгенерированный текст поста...",
    "status": "PUBLISHED",
    "post_type": "IMAGE",
    "published_at": "2026-04-05T12:00:00+05:00",
    "media": {{
      "post_type": "IMAGE",
      "images": [
        {{
          "url": "https://cdn.example.com/image.png?token=...",
          "is_selected": true,
          "in_album": false,
          "source": "ai_generated"
        }}
      ],
      "video": null,
      "documents": [],
      "expires_in": 86400
    }}
  }}
}}
Ссылки на медиа-файлы действительны ограниченное время (expires_in секунд, по умолчанию 24 часа). Скачайте файлы на свой сервер до истечения срока.

Типы событий

post.published

Пост успешно сгенерирован и отправлен через webhook

post.failed_quota

Генерация не выполнена - лимит тарифа исчерпан

Проверка HMAC-подписи

Каждый webhook-запрос подписывается HMAC-SHA256. Проверяйте подпись для защиты от подделки.

При каждом POST-запросе Taipy отправляет заголовки:

X-Taipy-Signature: sha256=<hex-дайджест>
X-Taipy-Event: post.published

Подпись вычисляется как HMAC-SHA256(секрет, тело_запроса). Ваш секрет - значение из поля «Токен бота» при создании Webhook-канала.

Пример: Python (Flask)

import hmac, hashlib, requests

def verify_signature(payload_body: bytes, secret: str, signature_header: str) -> bool:
    expected = hmac.new(
        secret.encode("utf-8"),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    received = signature_header.replace("sha256=", "")
    return hmac.compare_digest(expected, received)

@app.route("/webhook", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Taipy-Signature", "")
    if not verify_signature(request.data, "ваш_секрет", signature):
        return "Invalid signature", 403

    data = request.json
    post = data["data"]
    print(f"Новый пост: {{post['text'][:100]}}...")

    # Скачиваем медиа (ссылки временные - 24 часа!)
    media = post.get("media")
    if media:
        for img in media.get("images", []):
            resp = requests.get(img["url"], timeout=30)
            # Сохраните resp.content на свой сервер
        if media.get("video"):
            resp = requests.get(media["video"]["url"], timeout=120)
        for doc in media.get("documents", []):
            resp = requests.get(doc["url"], timeout=60)

    return "OK", 200

Пример: Node.js (Express)

const crypto = require("crypto");

function verifySignature(payload, secret, signatureHeader) {{
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  const received = signatureHeader.replace("sha256=", "");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}}

app.post("/webhook", express.raw({{ type: "*/*" }}), async (req, res) => {{
  const sig = req.headers["x-taipy-signature"] || "";
  if (!verifySignature(req.body, "ваш_секрет", sig)) {{
    return res.status(403).send("Invalid signature");
  }}
  const data = JSON.parse(req.body);
  const post = data.data;
  console.log(`Новый пост: ${{post.text.slice(0, 100)}}...`);

  if (post.media) {{
    for (const img of post.media.images || []) {{
      const resp = await fetch(img.url);
      // Сохраните resp.body на свой сервер
    }}
  }}
  res.sendStatus(200);
}});

Пример: PHP

<?php
function verifySignature(string $payload, string $secret, string $header): bool {{
    $expected = hash_hmac("sha256", $payload, $secret);
    $received = str_replace("sha256=", "", $header);
    return hash_equals($expected, $received);
}}

$payload = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_TAIPY_SIGNATURE"] ?? "";

if (!verifySignature($payload, "ваш_секрет", $signature)) {{
    http_response_code(403);
    die("Invalid signature");
}}

$data = json_decode($payload, true);
$post = $data["data"];
echo "Новый пост: " . mb_substr($post["text"], 0, 100) . "...\n";

// Скачиваем медиа (ссылки временные - 24 часа!)
if (isset($post["media"])) {{
    foreach ($post["media"]["images"] ?? [] as $img) {{
        $imageData = file_get_contents($img["url"]);
        // Сохраните $imageData на свой сервер
    }}
}}
http_response_code(200);