DokumentasiNotifikasi webhook
Notifikasi webhook
Terima notifikasi saat transkripsi selesai atau gagal. Verifikasi tanda tangannya sebelum memproses webhook.
Tambahkan webhook_url saat membuat transkripsi. Saat proses selesai atau gagal, transcript1 mengirim notifikasi ke URL tersebut lewat POST yang dilengkapi tanda tangan.
Gunakan REST API untuk webhook. Tool pada server MCP tidak menerima parameter ini.
Tentukan alamat penerima
Saat mengirim link video yang didukung atau menyiapkan unggahan bertahap lewat POST /v1/transcriptions, sertakan webhook_url pada body JSON. Jika mengunggah file dalam satu request, isi sebagai field form:
{
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"webhook_url": "https://example.com/hooks/transcript1"
}Pakai URL HTTPS tanpa nama pengguna atau kata sandi. Hasil resolusi URL harus berupa alamat publik. Alamat privat, loopback, link-local, dan alamat nonpublik lain mendapat 422 invalid_request. transcript1 memeriksanya saat transkripsi dibuat dan sebelum setiap pengiriman webhook.
Jangan gunakan webhook_url bersama reuse_recent=true; keduanya tidak bisa digabungkan.
Kapan webhook dikirim
type | Pemicu |
|---|---|
transcription.completed | Status transkripsi berubah menjadi completed. |
transcription.failed | Status transkripsi berubah menjadi failed. Baca code dan message di data.error. |
Jika transkripsi dibatalkan, tidak ada webhook yang dikirim.
Isi notifikasi
Webhook membawa body JSON berikut:
{
"type": "transcription.completed",
"created_at": "2026-10-13T09:30:04.000Z",
"data": {
"id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
"object": "transcription",
"status": "completed",
"transcript_source": "youtube_captions",
"billed_minutes": 4,
"error": null
}
}Field created_at mencatat kapan event dibuat. Blok data memuat objek transkripsi, tetapi tanpa transcript; contoh di atas disingkat. Ambil teksnya lewat GET /v1/transcriptions/:id menggunakan id dari data.
Setiap request memakai Content-Type: application/json dan User-Agent bernilai transcript1-webhooks/1.0. Ada juga header tanda tangan untuk verifikasi berikut.
Periksa tanda tangan sebelum membaca JSON
Baca tanda tangan dari header X-Transcript1-Signature pada setiap pengiriman:
X-Transcript1-Signature: t=<unix seconds>,v1=<hex digest>tmencatat waktu penandatanganan request sebagai Unix time dalam detik.v1berisi HMAC-SHA256 dalam format heksadesimal. Hitung dari string"<t>.<raw body>": gabungkan nilait, titik, lalu body request persis seperti yang diterima. Gunakan signing secret lengkap sebagai kunci, termasuk awalanwhsec_.
Verifikasi byte body asli sebelum melakukan parse JSON. Parse lalu serialisasi ulang bisa mengubah spasi atau urutan key, sehingga tanda tangan tidak lagi cocok. Bandingkan tanda tangan dengan metode constant-time. Tolak request jika nilai t terlalu jauh dari waktu sekarang; contoh berikut memakai toleransi 5 menit.
Node.js
import { Buffer } from 'node:buffer';
import crypto from 'node:crypto';
// header: X-Transcript1-Signature, rawBody: the request body exactly as received,
// secret: your full signing secret including the whsec_ prefix.
export const verifySignature = (secret, header, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) => {
const fields = Object.fromEntries(header.split(',').map(part => part.trim().split('=')));
const t = Number(fields.t);
if (!Number.isInteger(t) || !fields.v1 || Math.abs(nowSeconds - t) > 300) {
return false;
}
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return fields.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(fields.v1), Buffer.from(expected));
};Di Express, baca body sebagai teks mentah agar JSON belum diparse saat verifikasi:
import process from 'node:process';
import express from 'express';
import { verifySignature } from './verify-signature.js';
const app = express();
app.post('/hooks/transcript1', express.text({ type: 'application/json' }), (request, response) => {
const header = request.get('X-Transcript1-Signature') ?? '';
if (!verifySignature(process.env.TRANSCRIPT1_WEBHOOK_SECRET, header, request.body)) {
response.sendStatus(400);
return;
}
const { type, data } = JSON.parse(request.body);
console.log(type, data.id, data.status);
response.sendStatus(200);
});Python
import hashlib
import hmac
import time
def verify_signature(secret, header, raw_body, now=None):
# header: the X-Transcript1-Signature header, raw_body: the request body exactly as received,
# secret: your full signing secret including the whsec_ prefix.
fields = dict(part.strip().split("=", 1) for part in header.split(",") if "=" in part)
try:
t = int(fields["t"])
except (KeyError, ValueError):
return False
now = int(time.time()) if now is None else now
if abs(now - t) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.{raw_body}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(fields.get("v1", ""), expected)Di Flask, ambil body melalui request.get_data(as_text=True) dan teruskan sebagai raw_body.
Kelola signing secret tim
Signing secret berawalan whsec_ berlaku untuk seluruh tim. Semua webhook dari transkripsi tim memakai secret yang sama. Untuk melihat atau menggantinya, buka halaman “Webhooks” di /dashboard/webhooks. Penggantian berlaku saat itu juga. Semua webhook yang dikirim setelahnya memakai secret baru.
Respons, retry, dan notifikasi ganda
- Balas dengan status
2xxagar pengiriman dianggap berhasil. - Kode status lain, error koneksi, atau tidak ada respons dalam 10 detik dihitung sebagai percobaan gagal.
- Jika gagal, transcript1 mengirim ulang dengan jeda yang makin panjang (exponential backoff), maksimal 6 percobaan termasuk pengiriman pertama. Jedanya 2, 4, 8, 16, dan 32 menit. Percobaan terakhir berlangsung sekitar satu jam setelah yang pertama.
- Berikan URL tujuan akhir. transcript1 tidak mengikuti redirect; respons
3xxdihitung gagal. - Notifikasi yang sama bisa masuk lebih dari sekali, misalnya karena server Anda baru merespons setelah timeout. Periksa kombinasi
data.iddantypeuntuk mengabaikan duplikat.
Pembaruan terakhir 14 Oktober 2026