التوثيقإشعارات webhook
إشعارات webhook
استقبل إشعارا موقعا عند اكتمال التفريغ أو فشله، وتحقق من توقيعه قبل معالجته.
إذا أردت معرفة موعد انتهاء التفريغ دون الاستعلام المتكرر، أضف webhook_url عند إنشائه. نرسل إلى هذا الرابط طلب POST يحمل توقيعا عندما يكتمل التفريغ أو يفشل.
هذه الميزة متاحة عبر REST API فقط؛ لا تقبل أدوات خادم MCP معامل webhook.
أضف رابط استقبال الإشعارات
عند إرسال رابط فيديو مدعوم أو حجز رفع على أجزاء، ضع webhook_url في JSON المرسل إلى POST /v1/transcriptions. وإذا رفعت الملف بطلب واحد، ضعه في حقل من حقول النموذج:
{
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"webhook_url": "https://example.com/hooks/transcript1"
}استخدم رابط HTTPS دون اسم مستخدم أو كلمة مرور. يجب أن يشير إلى عنوان IP عام؛ لا نقبل عناوين الشبكات الخاصة أو loopback أو link-local أو أي عنوان غير عام. إذا لم يستوف الرابط هذه الشروط، تحصل على 422 invalid_request. نفحص العنوان عند إنشاء التفريغ وقبل كل محاولة إرسال.
لا ترسل webhook_url مع reuse_recent=true؛ لا يمكن استخدامهما معا.
متى يصلك الإشعار؟
type | سبب الإرسال |
|---|---|
transcription.completed | اكتمل التفريغ وأصبحت حالته completed. |
transcription.failed | فشل التفريغ وأصبحت حالته failed. تجد code وmessage في data.error. |
إذا ألغيت التفريغ، لا نرسل webhook.
محتوى الإشعار
يصلك body بصيغة JSON، كما في هذا المثال:
{
"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
}
}يبين created_at وقت إنشاء الحدث. يحتوي data على كائن التفريغ دون حقل transcript؛ عرضنا نسخة مختصرة منه أعلاه. لجلب النص المفرغ، استخدم id الموجود في data مع GET /v1/transcriptions/:id.
تتضمن الترويسات Content-Type: application/json، وUser-Agent بقيمة transcript1-webhooks/1.0، إضافة إلى ترويسة التوقيع التالية.
تحقق من التوقيع قبل قراءة JSON
نرسل مع كل إشعار ترويسة X-Transcript1-Signature بهذه الصيغة:
X-Transcript1-Signature: t=<unix seconds>,v1=<hex digest>tوقت توقيع الطلب، بصيغة Unix وبالثواني.v1توقيع HMAC-SHA256 بترميز hex. احسبه للسلسلة"<t>.<raw body>"، أي قيمةtثم نقطة ثم body كما وصل تماما. استخدم سر التوقيع كاملا كمفتاح، بما فيه البادئةwhsec_.
ابدأ بالبايتات الأصلية للطلب، قبل تحويل JSON إلى كائن. إعادة تحويل الكائن إلى JSON قد تغير المسافات أو ترتيب المفاتيح، وعندها لن يطابق التوقيع. استخدم مقارنة بزمن ثابت، وتحقق أيضا من قرب t من الوقت الحالي. الأمثلة التالية تقبل فارقا لا يتجاوز 5 دقائق.
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));
};في Express، اقرأ body كنص كما وصل، دون تحليل JSON مسبقا:
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)في Flask، استخدم request.get_data(as_text=True) للحصول على raw_body.
أين تجد سر التوقيع؟
افتح صفحة «Webhooks» في /dashboard/webhooks لعرض السر أو استبداله. بادئته whsec_، وهو سر واحد لجميع إشعارات التفريغ في فريقك. عند استبداله، يصبح السر الجديد ساريا فورا وتستخدمه كل الإشعارات التي نرسلها بعد ذلك.
الاستقبال وإعادة المحاولة
- أعد أي رمز
2xxلتأكيد وصول الإشعار. - نعتبر المحاولة فاشلة عند وصول رمز آخر، أو حدوث خطأ اتصال، أو عدم الرد خلال 10 ثوان.
- نكرر الإرسال بعد الفشل حتى 6 محاولات إجمالا. تتضاعف فترات الانتظار بين المحاولات إلى 2 ثم 4 ثم 8 ثم 16 ثم 32 دقيقة؛ فتأتي المحاولة الأخيرة بعد نحو ساعة من الأولى.
- استخدم الرابط النهائي مباشرة؛ لا يتبع transcript1 إعادة التوجيه، ويعامل
3xxكمحاولة فاشلة. - قد يصلك الإشعار نفسه أكثر من مرة، مثلا إذا أجاب خادمك بعد المهلة. اعتمد على
data.idوtypeلاكتشاف التكرار وتجاهله.
تاريخ التحديث 14 أكتوبر 2026