رفتن به محتوا

پرس‌وجوی دوره‌ای و فید رویدادها

لُب جلسه را در پس‌زمینه پردازش می‌کند، پس ابزار شما با پرسیدن از پیشرفت کار باخبر می‌شود. دو راه هست: وضعیت یک جلسه را بپرسید، یا فید رویدادها را بخوانید که می‌گوید در همهٔ جلسه‌هایتان چه چیزی تغییر کرده است.

GET /recordings/{id}/status را تقریباً هر یک دقیقه یک بار بخوانید. بیشتر جلسه‌ها ظرف چند دقیقه آماده می‌شوند، و پرسیدن در هر دقیقه بسیار کمتر از سقف دستهٔ read توکن است (سقف درخواست‌ها را ببینید).

  • اگر از سقف بگذرید، پاسخ 429 RATE_LIMITED با Retry-After است: ثانیه‌هایی که باید صبر کنید. همان‌قدر صبر کنید و زودتر دوباره نپرسید.
  • در NAMING یا READY بایستید: اولی به کار شما نیاز دارد و دومی پایان انتظار است. کاری که در هر کدام باید کرد در چرخهٔ یک جلسه آمده است.
  • FAILED ممکن است موقت باشد. لُب مرحلهٔ شکست‌خورده را پس از کمی صبر خودش دوباره امتحان می‌کند، پس ممکن است وضعیت را درست وقتی بخوانید که FAILED است و جلسه بعد پیش برود. اگر error.retryable برابر true است، جلسه را دوباره اجرا کنید و منتظر بمانید: تا وقتی اجرای خود لُب در راه است، تلاش دوبارهٔ شما اجرای دومی راه نمی‌اندازد، و 409 NOT_FAILED یعنی جلسه خودش درست شده است. پس از چند بار تلاش دوباره بایستید. اگر error.retryable برابر false است، بایستید و error.code را گزارش کنید. تلاش دوباره به recordings:write نیاز دارد.

حلقه‌ای که بین هر دو درخواست یک دقیقه صبر می‌کند، Retry-After را رعایت می‌کند، جلسه‌ای را که درست‌شدنی است دوباره اجرا می‌کند (حداکثر سه بار)، و با هر پاسخ دیگری آن را نشان می‌دهد و می‌ایستد (به jq نیاز دارد):

Terminal window
retries=0
while true; do
code=$(curl -sS -o status.json -D headers.txt -w '%{http_code}' \
https://app.lobbe.ai/api/v1/recordings/MEETING_ID/status \
-H "Authorization: Bearer $LOBBE_TOKEN") || break
wait=$(awk -F': *' 'tolower($1) == "retry-after" { print $2 + 0 }' headers.txt)
if [ "$code" = 429 ] && [ -n "$wait" ]; then sleep "$wait"; continue; fi
[ "$code" = 200 ] || { cat status.json; break; }
status=$(jq -r .status status.json)
echo "$status"
case "$status" in NAMING | READY) break ;; esac
if [ "$status" = FAILED ]; then
if [ "$(jq -r .error.retryable status.json)" != true ] || [ "$retries" -ge 3 ]; then
cat status.json
break
fi
retries=$((retries + 1))
code=$(curl -sS -o retry.json -w '%{http_code}' -X POST \
https://app.lobbe.ai/api/v1/recordings/MEETING_ID/retry \
-H "Authorization: Bearer $LOBBE_TOKEN") || break
# 200: the step runs again. 409 NOT_FAILED: it recovered by itself. Either way, keep waiting.
[ "$code" = 200 ] || [ "$(jq -r .error.code retry.json 2>/dev/null)" = NOT_FAILED ] ||
{ cat retry.json; break; }
fi
sleep 60
done

اگر بیش از چند جلسه را دنبال می‌کنید، به‌جای پرسیدن وضعیت تک‌تک آن‌ها GET /events را بخوانید. فید رویدادها همهٔ اتفاق‌هایی را که برای جلسه‌های شما افتاده، از قدیمی‌ترین، پشت سر هم می‌آورد:

{
"schemaVersion": 1,
"events": [
{
"id": "1042",
"type": "recording.naming_required",
"recordingId": "MEETING_ID",
"seriesId": null,
"occurredAt": "2026-10-19T09:14:03.120Z",
"data": { "status": "NAMING" }
}
],
"nextCursor": "773.1042.1792300000000"
}
type چه زمانی فرستاده می‌شود data
recording.created لُب جلسه را کامل دریافت کرده است: ضبط‌شده در برنامه، بارگذاری‌شده یا فرستاده‌شده با توکن status
recording.naming_required جلسه در NAMING است؛ هر بار که نام‌گذاری دوباره باز شود دوباره فرستاده می‌شود status
recording.ready لُب جلسه برای نخستین بار ساخته شد status
recording.failed جلسه به‌طور قطعی ناموفق شد status و error: { code, retryable }
recording.updated جلسه‌ای READY تغییر کرد و data.change می‌گوید چگونه status و change
recording.deleted جلسه حذف شد، در هر وضعیتی status

data.change یکی از این‌هاست: regenerated (لُب دوباره ساخته شد)، renamed، transcript_edited، speakers_renamed، series_changed (جلسه به سری‌ای رفت، از سری بیرون آمد یا سری‌اش عوض شد) و restored (جلسهٔ حذف‌شده با همان وضعیتی که داشت برگشت).

  • هیچ رویدادی محتوای جلسه را ندارد: فقط شناسه‌ها، وضعیت جلسه، کد خطا یا نوع تغییر. برای خواندن خود جلسه، جلسهٔ کامل را بگیرید.
  • مرحله‌های میانی رویدادی ندارند: QUEUED، TRANSCRIBING و GENERATING چیزی نمی‌فرستند. تلاش دوباره هم رویدادی ندارد و رویداد بعدی جلسه از پی آن می‌آید.
  • recording.failed قطعی است: شکستی که لُب هنوز خودش دوباره امتحانش می‌کند هرگز فرستاده نمی‌شود.
  • ساخت دوبارهٔ لُب recording.updated است با regenerated، هرگز یک recording.ready دیگر.
  • تغییر خود سری برای جلسه‌هایش رویدادی ندارد: تغییر نام سری، تغییر شیوهٔ عنوان‌گذاری جلسه‌ها یا حذف سری.
  • با recordings:read، فید رویدادهای همهٔ جلسه‌های حساب را دارد، هر که فرستاده باشد: خود برنامه، بارگذاری، همین توکن یا توکنی دیگر. جلسه‌های سری‌ای که صاحب حساب عضوش است در آن نیستند.
  • با recordings:write و بدون recordings:read، فقط جلسه‌هایی را دارد که همین توکن فرستاده است. تا وقتی فرستادن جلسه با توکن برای حساب روشن نشده، چنین توکنی پاسخ 403 SCOPE_DISABLED می‌گیرد.
  • seriesId سری‌ای است که جلسه هنگام تغییر در آن بود؛ اگر در هیچ سری‌ای نبود یا سری‌ها خاموش باشند، null است.
  1. از همین حالا شروع کنید با after=latest: رویدادی برنمی‌گرداند و نشانگری می‌دهد که از آن به بعد بخوانید. بدون after، فید از قدیمی‌ترین رویدادی که هنوز نگه داشته شروع می‌شود، یعنی حدود ۳۰ روز پیش.
  2. nextCursor را نگه دارید. همیشه در پاسخ هست. آن را در خواندن بعدی، بی‌هیچ تغییری، به‌عنوان after بفرستید: هرگز نشانگری نسازید یا تغییر ندهید، و id رویداد را هم به‌جای نشانگر به کار نبرید.
  3. صفحه به صفحه بخوانید. هر صفحه حداکثر limit رویداد دارد (از ۱ تا ۵۰۰، به‌طور پیش‌فرض ۱۰۰). اگر صفحه پر برگشت، صفحهٔ بعد را همان لحظه بخوانید؛ وگرنه حدود یک دقیقه صبر کنید.
  4. نشانگر را بعد از پردازش رویدادهای صفحه ذخیره کنید، هرگز پیش از آن.

حلقه‌ای که هر چهار کار را می‌کند و با هر خطایی آن را نشان می‌دهد و می‌ایستد:

Terminal window
cursor=$(cat lobbe-cursor 2>/dev/null || echo latest)
while true; do
body=$(curl -sS --fail-with-body "https://app.lobbe.ai/api/v1/events?after=$cursor&limit=100" \
-H "Authorization: Bearer $LOBBE_TOKEN") || { echo "$body"; break; }
jq -c '.events[]' <<<"$body" # handle each event here
cursor=$(jq -er .nextCursor <<<"$body") || { echo "$body"; break; }
echo "$cursor" >lobbe-cursor
[ "$(jq '.events | length' <<<"$body")" -eq 100 ] || sleep 60
done

برنامه‌ای که پیوسته اجرا می‌شود، با پاسخ 429 به‌جای ایستادن به اندازهٔ Retry-After صبر می‌کند و پس از 410 به روشی که پایین‌تر آمده کارش را از سر می‌گیرد. خواندن فید رویدادها از همان سقف read خرج می‌کند که خواندن وضعیت، جلسهٔ کامل و فهرست جلسه‌ها.

فید رویدادها هر رویداد را دست‌کم یک بار به شما می‌دهد، نه دقیقاً یک بار. اگر برنامهٔ شما پس از پردازش یک صفحه و پیش از ذخیرهٔ نشانگرش متوقف شود، خواندن بعدی همان رویدادها را دوباره برمی‌گرداند.

  • رویدادهایی را که پردازش کرده‌اید با id کنار بگذارید. شناسهٔ رویداد در هر بار خواندن یکسان است.
  • تکرار را با جلسه و نوع رویداد تشخیص ندهید. یک جلسه ممکن است چند رویداد از یک نوع داشته باشد: نام‌گذاری ممکن است دو بار باز شود و نام جلسه ممکن است بارها تغییر کند. هر کدام رویداد جداگانه‌ای است با id خودش.
  • ترتیب فید را دنبال کنید، نه occurredAt را. رویدادهای هر جلسه به همان ترتیبی می‌آیند که تغییرهایش رخ داده است. رویداد ممکن است کمی دیرتر از خود تغییر برسد، اما هیچ رویدادی جا نمی‌افتد.
  • لُب رویدادها را ۳۰ روز نگه می‌دارد.
  • عمر نشانگر از آخرین رویدادی حساب می‌شود که خوانده است، یا وقتی به آخر فید رسیده‌اید، از خود همان خواندن. پس برنامه‌ای که مرتب می‌خواند هیچ‌وقت کهنه نمی‌شود، حتی در حسابی که اتفاقی در آن نمی‌افتد.
  • نشانگری که از این مدت قدیمی‌تر باشد (مثلاً نشانگری که یک ماه بی‌استفاده مانده) پاسخ 410 CURSOR_EXPIRED می‌گیرد. نشانگری که به پیش از بازگردانی داده‌های لُب برگردد هم همین‌طور.

برای ادامه دادن پس از 410، یا شروع کار روی حسابی که از پیش جلسه دارد:

  1. نشانگر تازه‌ای بگیرید، با after=latest.
  2. عقب‌ماندگی را جبران کنید. با دسترسی recordings:read، فهرست جلسه‌های آماده را صفحه به صفحه از GET /recordings/ready بخوانید و جلسه‌های آماده‌ای را که هنوز ندارید اضافه کنید. این فهرست فقط جلسه‌های READY را دارد، پس جلسه‌ای که در این فاصله به‌طور قطعی ناموفق یا حذف شده در آن نیست. برای جلسه‌ای که دارید اما دیگر در این فهرست نیست، وضعیتش را بپرسید: 404 یعنی حذف شده است، وگرنه وضعیتش می‌گوید اکنون در چه مرحله‌ای است (مثلاً لُبش دوباره ساخته می‌شود). برای جلسه‌ای که هنوز منتظرش بودید، وضعیتش می‌گوید ناموفق شده یا نه. توکنی که فقط recordings:write دارد این فهرست را ندارد: وضعیت هر جلسه‌ای را که فرستاده و هنوز منتظرش هستید بپرسید.
  3. از نشانگر تازه به بعد بخوانید.

با این ترتیب، جلسه‌ای که در همان مدتِ جبران عقب‌ماندگی تغییر کند، در فید رویدادها هم می‌آید. ممکن است آن را دو بار ببینید، اما هرگز از دستش نمی‌دهید.