آوردن جلسهها به ابزارهای خودتان
توکنی که recordings:read («خواندن» در گروه «جلسهها») دارد میتواند همهٔ جلسههای آماده را از لُب
بیرون بیاورد: به پوشهای از یادداشتهای Markdown، یک ویکی، یک نمایهٔ جستوجو، یا صندوق ورودیای که یک
دستیار کارهایش را از آن برمیدارد. این صفحه این کار را قدم به قدم پیش میبرد و بعد نشان میدهد چطور آن
را بهروز نگه دارید.
فهرست را بخوانید و هر جلسه را بگیرید
Section titled “فهرست را بخوانید و هر جلسه را بگیرید”- فهرست جلسههای آماده را صفحه به صفحه بخوانید، با
GET /recordings/ready: از تازهترین، و در هر صفحه حداکثرlimitجلسه (از ۱ تا ۱۰۰، بهطور پیشفرض ۲۰). مقدارnextCursorهر صفحه را، بیهیچ تغییری، بهعنوانcursorبرگردانید؛ در صفحهٔ آخرnullاست. - هر جلسهای را که هنوز ندارید از
bundleUrlآن بگیرید: بهصورت JSON، یا با?format=markdownبهصورت یک سند Markdown. - آن را با
idجلسه نگه دارید. این شناسه هرگز تغییر نمیکند.
{ "schemaVersion": 1, "items": [ { "id": "MEETING_ID", "title": "Weekly product sync", "kind": "MEETING", "createdAt": "2026-10-19T08:31:12.000Z", "recordedAt": "2026-10-19T07:00:00.000Z", "durationSeconds": 2520, "url": "https://app.lobbe.ai/m/MEETING_ID", "bundleUrl": "https://app.lobbe.ai/api/v1/recordings/MEETING_ID/bundle", "series": null, "displayTitle": "Weekly product sync" } ], "nextCursor": "1792398672000.MEETING_ID"}سه نکته دربارهٔ این فهرست:
- هر بار همهٔ فهرست را بخوانید. با رسیدن به نخستین جلسهای که از پیش دارید نایستید. ترتیب فهرست ترتیب دریافت جلسهها به دست لُب است و هر جلسه فقط وقتی آماده شد به فهرست میآید: جلسهٔ بلندی که ساعت ۱۰:۰۰ فرستاده شده ممکن است دیرتر از جلسهٔ کوتاهی که ساعت ۱۰:۰۵ فرستاده شده آماده شود و آنوقت پایینتر از آن مینشیند.
- نشانگر جای خودش را نگه میدارد. از پس آخرین جلسهٔ همان صفحه ادامه میدهد، پس جلسهای که میان دو صفحه از فهرست بیرون برود باعث نمیشود جلسهٔ بعدی را از دست بدهید.
- فقط جلسههای خود شما در آن است. سریای که عضوش شدهاید در آن نیست: برای هر کدام از آن سریها
GET /recordings/ready?seriesId=SERIES_IDرا بخوانید (سری جلسات را ببینید).
این فهرست نمیگوید جلسهای که کپی کردهاید تغییر کرده است (لُبش دوباره ساخته شده، عنوانش عوض شده، رونوشتش ویرایش شده، نام گویندههایش تغییر کرده یا به سری دیگری رفته) یا حذف شده است؛ اما فید رویدادها این را میگوید.
همگام ماندن با فید رویدادها
Section titled “همگام ماندن با فید رویدادها”بهجای اینکه کل فهرست را بارها از نو بخوانید، GET /events
(فید رویدادها) را دنبال کنید. فید رویدادها همان
جلسههایی را در بر میگیرد که فهرست بدون seriesId دارد، یعنی جلسههای خود شما، در هر وضعیتی.
- اول یک نشانگر بگیرید، با
GET /events?after=latest، و نگهش دارید. - بعد یک بار کل فهرست را بخوانید، همانطور که بالاتر آمد، برای جلسههایی که از پیش هستند.
- بعد فید رویدادها را از همان نشانگر بخوانید. هر چه در مدت خواندن فهرست تغییر کرده در فید هست، پس هیچ جلسهای را از دست نمیدهید. ممکن است جلسهای را دو بار ببینید، که اشکالی ندارد.
با هر رویداد چه کنید:
| رویداد | چه کنید |
|---|---|
recording.ready |
جلسهٔ کامل آن را بگیرید |
recording.updated |
جلسهٔ کامل آن را دوباره بگیرید: data.change میگوید چه چیزی تغییر کرده است |
recording.deleted |
کپی خودتان را حذف کنید |
recording.created، recording.naming_required و recording.failed |
کاری لازم نیست: جلسه هنوز آماده نیست |
- وقتی
data.statusبرابرREADYاست، جلسه را بگیرید. جلسهٔ حذفشدهای که برگردانده شود باrecording.updatedوrestoredبرمیگردد، با همان وضعیتی که داشت: اگر آن وضعیتREADYاست، آن را بگیرید. - ممکن است جلسهٔ کامل پاسخ
409 NOT_READYبدهد، اگر وقتی سراغش میروید لُبش دوباره در حال ساخته شدن باشد، یا پاسخ404، اگر در این فاصله حذف شده باشد. از آن بگذرید: رویداد بعدی میگوید چه شد. - دو بار گرفتن یک جلسه اشکالی ندارد. فید رویدادها هر رویداد را دستکم یک بار میدهد؛ دستکم یک بار را ببینید.
- نشانگری که ۳۰ روز بیاستفاده بماند پاسخ
410 CURSOR_EXPIREDمیگیرد: از گام ۱ از نو شروع کنید. خواندن فهرست از جلسههایی که از پیش دارید میگذرد، پس جلسهای که در غیاب شما تغییر کرده کپی قدیمیاش را نگه میدارد. برای اطمینان، کپی خودتان را خالی کنید و همه را دوباره بگیرید.
اسکریپتی که همهٔ این کارها را میکند
Section titled “اسکریپتی که همهٔ این کارها را میکند”این اسکریپت همهٔ جلسههای آماده را در پوشهٔ ./meetings کپی میکند، برای هر جلسه یک فایل Markdown، و
بعد برای همیشه فید رویدادها را دنبال میکند. به bash، curl و jq نیاز دارد. اگر بایستد و دوباره
اجرایش کنید، از نشانگری که ذخیره کرده ادامه میدهد، حتی اگر در میانهٔ نخستین خواندن فهرست ایستاده باشد؛ و با هر پاسخی که انتظارش را ندارد، همان پاسخ را
نشان میدهد و میایستد.
#!/usr/bin/env bash# Copy every finished meeting into ./meetings as Markdown, then keep up with the feed.api=https://app.lobbe.ai/api/v1mkdir -p meetings
# GET $1 into the file $2 and print the HTTP status (000: no answer). Waits Retry-After on a 429.get() { local code wait while true; do rm -f "$2" headers.txt code=$(curl -sS -o "$2" -D headers.txt -w '%{http_code}' "$1" \ -H "Authorization: Bearer $LOBBE_TOKEN") || code=000 wait=$(awk -F': *' 'tolower($1) == "retry-after" { print $2 + 0 }' headers.txt 2>/dev/null) if [ "$code" = 429 ] && [ -n "$wait" ]; then sleep "$wait"; continue; fi echo "$code" return done}
# One meeting's Markdown. 404 (deleted) and 409 (being made again) are skipped:# a later event says what happened. Anything else stops the script.fetch() { case "$(get "$api/recordings/$1/bundle?format=markdown" meeting.tmp)" in 200) mv meeting.tmp "meetings/$1.md" ;; 404 | 409) rm -f meeting.tmp ;; *) cat meeting.tmp 2>/dev/null; exit 1 ;; esac}
# The first run: a cursor from now, THEN the whole list, so nothing that changes meanwhile is missed.# A run stopped during the walk keeps that first cursor, so the feed still covers what changed since.if [ ! -s meetings/.cursor ]; then if [ ! -s meetings/.start-cursor ]; then [ "$(get "$api/events?after=latest" page.json)" = 200 ] || { cat page.json 2>/dev/null; exit 1; } jq -er .nextCursor page.json >next.cursor && mv next.cursor meetings/.start-cursor || exit 1 fi cursor="" while true; do [ "$(get "$api/recordings/ready?limit=100${cursor:+&cursor=$cursor}" page.json)" = 200 ] || { cat page.json 2>/dev/null; exit 1; } for id in $(jq -r '.items[].id' page.json); do [ -f "meetings/$id.md" ] || fetch "$id" done cursor=$(jq -r '.nextCursor // empty' page.json) [ -n "$cursor" ] || break done mv meetings/.start-cursor meetings/.cursorfi
# Then the feed, page by page, storing the cursor only once a page is handled.while true; do [ "$(get "$api/events?after=$(cat meetings/.cursor)&limit=100" page.json)" = 200 ] || { cat page.json 2>/dev/null; exit 1; } jq -r '.events[] | "\(.type) \(.recordingId) \(.data.status)"' page.json >events.txt while read -r type id status; do if [ "$type" = recording.deleted ]; then rm -f "meetings/$id.md" elif [ "$status" = READY ]; then fetch "$id" fi done <events.txt jq -er .nextCursor page.json >next.cursor && mv next.cursor meetings/.cursor || exit 1 [ "$(jq '.events | length' page.json)" -eq 100 ] || sleep 60done- پس از
410 CURSOR_EXPIRED: فایلهایmeetings/.cursorوmeetings/.start-cursorرا حذف کنید و اسکریپت را دوباره اجرا کنید. نشانگر تازهای میگیرد و فهرست را دوباره میخواند، و فقط جلسههایی را میگیرد که ندارد. - فایلهای خود اسکریپت: دو نشانگر کنار کپیها در
meetings/میمانند. فایلهایpage.json،events.txtوheaders.txtهم در پوشهای میمانند که اسکریپت را از آن اجرا میکنید، و اگر گرفتن جلسهای اسکریپت را متوقف کرده باشد،meeting.tmpهم. - JSON بهجای Markdown:
?format=markdownرا بردارید و پسوند فایلها را.jsonبگذارید. - سقف درخواستها: فهرست، جلسههای کامل و فید رویدادها همه از سقف دستهٔ
readتوکن خرج میکنند؛ اندازهٔ این سقف در سقف درخواستها آمده است. در نخستین اجرا، آرشیو بزرگ ممکن است این سقف را پر کند؛ آنوقتgetبه اندازهٔ ثانیههایی کهRetry-Afterمیگوید صبر میکند. - هر توکن برای یک کار: این توکن فقط
recordings:readلازم دارد و هرگز دقیقهای خرج نمیکند.
جلسهٔ کامل چه چیزهایی دارد
Section titled “جلسهٔ کامل چه چیزهایی دارد”GET /recordings/{id}/bundle یک جلسهٔ آماده را
برمیگرداند. بهصورت JSON چهار بخش دارد:
| بخش | چه چیزی در آن است |
|---|---|
recording |
خود جلسه: شناسه، عنوانها (title، displayTitle و subtitle)، kind، source، زمان برگزاری (recordedAt) و زمان دریافت به دست لُب (createdAt)، مدت، url آن در برنامه، series آن، و access: owner، یا member برای سریای که عضوش هستید |
transcript |
آنچه گفته شد: زبان، آدمها (speakers، با نامهایی که به آنها داده شده)، نوبتها (segments، هر کدام با گوینده، زمانها و متنش)، کل متن، و رونوشت بهصورت Markdown |
lobbe |
لُب جلسه: قالب آن (type)، زبانش، content آن بهصورت بخشها، و کل لُب بهصورت Markdown |
stats |
عددهای جلسه که از روی رونوشت شمرده شدهاند: زمان صحبت، نوبتها و سؤالها، برای هر نفر |
بخشهای لُب به ترتیب خواندن میآیند و kind هر بخش میگوید چه فیلدهایی دارد: summary،
decision_list، action_item_list و بقیه. مرجع API فیلدهای هر نوع را فهرست کرده است.
با ?format=markdown همان جلسه یک سند text/markdown است: عنوان بهصورت تیتر اصلی، مشخصات جلسه (زمان،
مدت، سری و گویندهها)، بخشهای لُب و سپس رونوشت. طوری ساخته شده که همانطور که هست در یک یادداشت یا در
ورودی یک دستیار هوش مصنوعی بنشیند. همان دسترسی و خطاهای نسخهٔ JSON را دارد: 404 برای جلسهای که
نمیتوانید بخوانید و 409 NOT_READY برای جلسهای که هنوز آماده نیست.
با انعطاف بخوانید
Section titled “با انعطاف بخوانید”- ساختار جلسهٔ کامل در
v1ثابت میماند.schemaVersionهمان1میماند و تغییرهای درونv1فقط چیزی اضافه میکنند: هیچ فیلدی حذف نمیشود یا تغییر نام نمیدهد. - پس آنچه را نمیشناسید نادیده بگیرید. فیلدی که نمیشناسید ممکن است در هر بخشی بیاید: از آن بگذرید و
هرگز به خاطرش خطا ندهید. بخشی از لُب هم که
kindآن را نمیشناسید همینطور. - کپی خود را با
recording.idنگه دارید و آن را بر اساسrecordedAt، یعنی زمان برگزاری جلسه، مرتب کنید.