Перейти к содержимому

Webhooks

Если при создании задачи передать webhook_url, после завершения задачи (статус done или failed) мы отправим на этот адрес POST-запрос с объектом задачи в теле — тем же, что возвращает GET /media/tasks/:id.

Тело запроса

Тело — JSON-объект задачи (см. Получение задачи). Пример:

{
"code": 0,
"id": "6a61e021a6e77553a989c41d",
"model": "kling-v3",
"status": "done",
"result": [ { "url": "https://.../6a61e021a6e77553a989c41d_result.mp4" } ],
"created_at": 1784799265
}

Заголовки

ЗаголовокЗначение
X-Gptunnel-EventТип события: task.done или task.failed
X-Gptunnel-DeliveryИдентификатор задачи
X-Gptunnel-SignatureПодпись тела: t=<unix>,v1=<hex>

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

Подпись позволяет убедиться, что запрос пришёл от нас. Формат заголовка совместим со схемой Stripe:

X-Gptunnel-Signature: t=1784799265,v1=5f8c...e21a

Где t — момент подписи (Unix, секунды), v1HMAC-SHA256 в hex от строки "<t>.<тело запроса>". Секрет подписи — ваш API-ключ.

Чтобы проверить подпись: возьмите значение t и сырое тело запроса, вычислите HMAC-SHA256 от строки `${t}.${rawBody}` с вашим API-ключом в качестве секрета и сравните с v1. Рекомендуется также отклонять запросы со слишком старым t.

import { createHmac, timingSafeEqual } from 'crypto'
function verify(rawBody: string, header: string, apiKey: string): boolean {
const parts = Object.fromEntries(
header.split(',').map((kv) => kv.split('=')),
)
const expected = createHmac('sha256', apiKey)
.update(`${parts.t}.${rawBody}`)
.digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(parts.v1 ?? '')
return a.length === b.length && timingSafeEqual(a, b)
}

Повторные попытки

Ваш эндпоинт должен ответить статусом 2xx. Если доставка не удалась, мы повторим её несколько раз с нарастающими паузами (примерно через 5 с, 20 с, 60 с и 180 с).