هر آنچه برای رفتن از یک لینک ساده به یک قابلیت واقعی در محصولتان لازم دارید: مفاهیم، دو جریان یکپارچهسازی گامبهگام، نکات امنیتی و جدول عیبیابی خطاها. این راهنما را کنار مرجع API بخوانید تا شکل دقیق پاسخها را داشته باشید.
هر دو سطح در نهایت یک چیز میسازند: توکن LiveKit برای wss://rtc.videorayan.ir. از هرکدام که امروز به کارتان میآید شروع کنید؛ بعداً بدون تغییر کد کلاینت میتوانید عوضش کنید.
با curl یا صفحهٔ ساخت جلسه یک اتاق بسازید، لینک /r/{roomId} را به اشتراک بگذارید — تمام. مهمانها فقط نام نمایشی میدهند و وارد میشوند. مناسب جلسات، مشاورهها و ابزارهای داخلی.
بکاند شما اتاقهای اجارهای میسازد، برای هر کاربر توکن host/guest ضرب میکند و چرخهٔ عمر را کنترل میکند. مناسب محصولاتی که تماس در آنها «قابلیت» است، نه فقط یک لینک.
# create a room and open the hosted UI — that's the whole demo ROOM=$(curl -s -X POST https://api.videorayan.ir/v1/public/rooms \ -H "Content-Type: application/json" -d '{"name":"my-first-room"}' | jq -r .roomId) open "https://videorayan.ir/r/$ROOM" # macOS (linux: xdg-open)
این چهار تا را بفهمید؛ بقیه فقط پارامتر است.
ظرف جلسه با چرخهٔ عمر (active → ended/expired). اتاق رایگان: نامش دستگیرهٔ عمومی است و ۱۲ ساعت زنده میماند. اتاق اجارهای: UUID بههمراه externalId خودتان، با عمر تا ۲۴ ساعت.
راه آدرسدادن به اتاق: UUID آن، یا برای اتاقهای عمومی، نامش. در /v1/public/rooms/{ref}، /v1/guest/rooms/{ref}/tokens و رابط میزبانیشدهٔ /r/{ref} کار میکند.
یک JWT امضاشدهٔ LiveKit که دقیقاً به یک اتاق محدود است و حداقل دسترسیها را دارد. پیشفرض حدود ۱۰ دقیقه اعتبار دارد و قابل تمدید نیست — بهازای هر جلسه تازه ضرب کنید و مثل یک اعتبارنامه با آن رفتار کنید.
نام یکتای شرکتنده درون اتاق (۱ تا ۴۰ نویسه در سطح مهمان). شناسهٔ تکراری در یک اتاق ممکن نیست — اتصال جدیدتر جایگزین قدیمیتر میشود.
host یا guest باشند؛ توکنهای سطح مهمان همیشه guestاند. نقشها سمت سرور به دسترسیهای LiveKit نگاشت میشوند — کلاینت نمیتواند خودش را ارتقا دهد.این دقیقاً همان جریانی است که صفحهٔ پیوستنِ میزبانیشده پیاده کرده — وقتی صفحهٔ ورود خودتان را میسازید همین را بازسازی کنید.
GET /v1/public/rooms/{ref} ← roomId واقعی، name و اینکه needsPassword true است یا نه. فیلد رمز را فقط در صورت نیاز نشان دهید.getUserMedia دوربین/میکروفن را request کنید تا کاربر قبل از ورود پیشنمایش ببیند؛ انتخابش را یاد بگیرید و بعد از اتصال اعمال کنید.POST /v1/guest/rooms/{roomId}/tokens با identity (و password در صورت نیاز).201 ← اتصال؛ 403 wrong_password ← رمز را دوباره بپرسید؛ 409 ← اتاق پر/پایانیافته؛ 404 ← لینک نامعتبر، کاربر را به خانه بفرستید.new Room().connect('wss://rtc.videorayan.ir', token) — برای خطاهای گذرا یک تلاش مجدد کافی است.const API = 'https://api.videorayan.ir/v1'; async function joinRoom(ref, identity, password) { // 1 — resolve (validates the link before asking the user anything) const info = await fetch(`${API}/public/rooms/${encodeURIComponent(ref)}`).then(r => r.json()); if (!info.roomId) throw new Error('room_not_found'); // 2 + 3 — mint the token (password only when the room needs one) const res = await fetch(`${API}/guest/rooms/${info.roomId}/tokens`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identity, ...(info.needsPassword ? { password } : {}) }) }); // 4 — handle outcomes explicitly if (res.status === 403) throw new Error('wrong_password'); if (res.status === 409) throw new Error('room_full_or_ended'); if (!res.ok) throw new Error('token_failed_' + res.status); const { token } = await res.json(); // 5 — connect (single retry on transient failure) const { Room } = await import('livekit-client'); const room = new Room({ adaptiveStream: false, dynacast: true }); await room.connect('wss://rtc.videorayan.ir', token); return room; }
کلید API فقط در محیط بکاند شما زندگی میکند — مرورگر هرگز آن را نمیبیند.
Idempotency-Key اتاق بسازید تا retry هرگز اتاق دوبله نسازد.wsUrl را از بکاند خودتان بگیرید و به SDK بدهید.DELETE /v1/rooms/{roomId} همه را قطع میکند، توکنهای جدید را رد میکند و ورود مجدد را میبندد (وضعیت: ended یا با sweeper: expired).async function ensureRoom(appointmentId) { const res = await fetch('https://api.videorayan.ir/v1/rooms', { method: 'POST', headers: { Authorization: `Bearer ${process.env.VIDEORAYAN_KEY}`, // backend only 'Content-Type': 'application/json', 'Idempotency-Key': `apt-${appointmentId}` // same key → same room }, body: JSON.stringify({ externalId: appointmentId, // your correlation ID maxParticipants: 3, expiresInSeconds: 3600 // 1-hour window }) }); if (!res.ok) throw new Error('room create failed: ' + res.status); return res.json(); // { roomId, status, ... } }
async function joinAs(roomId, user) { const res = await fetch(`https://api.videorayan.ir/v1/rooms/${roomId}/tokens`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.VIDEORAYAN_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ identity: user.id, // unique per room name: user.name, role: user.isHost ? 'host' : 'guest', ttlSeconds: 600 }) }); return res.json(); // { token, wsUrl, identity, expiresAt } }
چند تمرین ساده سمت شما، مدل امنیتی پلتفرم را کامل میکند.
کلیدهای اجارهای rtck_… هرگز نباید در کد فرانتاند، باندل موبایل یا گیت ظاهر شوند. اگر لو رفت، از طریق اپراتور بچرخانیدش (rotate).
اتاقهای خصوصیِ عمومی، رمز را هنگام صدور توکن بررسی میکنند — کلاینت نمیتواند دورش بزند. خطا هم قطعی است: 403 wrong_password.
دسترسی بیناجارهای همان پاسخ «وجود ندارد» را میدهد: 404 room_not_found. UIای نسازید که به تمایز این دو وابسته باشد.
رسانه با DTLS-SRTP بین شرکتکنندگان و SFU خودمیزبان محافظت میشود و سیگنالینگ روی WSS میرود. E2EE در نسخهٔ اول فعال نیست — به کاربران چنین ادعایی نکنید.
بدنههای خطا قطعیاند — روی فیلد error در JSON شاخه بزنید.
| نشانه | علت | راهحل |
|---|---|---|
429 هنگام ساخت اتاق | سقف نرخ هر IP پر شده (NAT مشترک، تست بار) | بکآف نمایی؛ برای تست بار از اپراتور کلید اجارهای اختصاصی بخواهید |
403 روی توکن مهمان | رمز اتاق اشتباه یا ناموجود | اول اتاق را resolve کنید و وقتی needsPassword true است رمز بگیرید |
409 روی توکن | اتاق پر (سقف) یا پایانیافته/منقضی | «اتاق پر است / تمام شده» را نشان دهید؛ برای جلسهٔ بعد اتاق تازه بسازید |
404 روی resolve | لینک بد یا اتاق منقضی (پیشفرض ۱۲ ساعت برای اتاقهای رایگان) | اتاق تازه بسازید؛ room_not_found را retryable ندانید |
400 روی ساخت | اعتبارسنجی بدنه رد شده (طول فیلدها، فیلد ناشناخته) | بدنهٔ invalid_input را طبق مستندات اصلاح کنید |
| توکن محلی کار میکند، در تولید نه | توکن برای اتاق دیگری ضرب شده یا در مسیر منقضی شده | بهازای هر ورود ضرب کنید و فوراً مصرفش کنید |
| رسانه وصل است ولی صدای یک نفر نیست | میکروفن مجوز نداده یا در سطح OS بیصدا است | خطاهای getUserMedia را بررسی کنید؛ پلتفرم در هر ورود صدا را request میکند |
نه. سطح عمومیِ اتاقها باز است: با یک نام اتاق بسازید، لینک /r/{roomId} را به اشتراک بگذارید و مهمانها با نام نمایشی وارد میشوند. کلید فقط برای اتاقهای اجارهای با نقش و سقف سمت سرور لازم است.
پیشفرض حدود ۱۰ دقیقه — زمان دقیق را در فیلد expiresAt پاسخ ببینید. توکن قابل تمدید نیست؛ برای هر جلسه از بکاند (یا اندپوینت مهمان برای اتاقهای رایگان) توکن تازه ضرب کنید.
اتاقهای اجارهای در حال حاضر تا ۳ شرکتکننده — و برای کاربردهای واقعی از طریق درخواست منابع قابل افزایش است. اتاقهای رایگان طبق سیاست رسانهای فعلی سرو میشوند.
بله — همهٔ رسانه با DTLS-SRTP بین شرکتکنندگان و SFU خودمیزبان محافظت میشود و سیگنالینگ روی WSS اجرا میشود. E2EE در نسخهٔ اول فعال نیست.
بله. یا به رابط میزبانیشدهٔ /r/{roomId} لینک دهید، یا با هر SDK سازگار LiveKit و توکنی که بکاند شما ضرب کرده، مستقیم از فرانتاند خودتان وصل شوید — هر دو از همان اتاقها استفاده میکنند.
یک sweeper هر ۶۰ ثانیه اتاقهای منقضی را علامت میزند و از سرور رسانه حذف میکند، پس توکنهای کهنه دیگر ورود نمیکنند. اتاقهای رایگان ۱۲ ساعت زنده میمانند و چون ساخت با نام idempotent است، ورود بعدی دوباره همان اتاق را میسازد — برای جلسهٔ بعدی فقط دوباره سر بزنید.
سؤالتان در مستندات جواب نگرفت؟ به اتاق اجتماعی بیایید — صدا، تصویر و چتِ مرورگری بدون حساب. هر نکته یا کمبودی هم دیدید، خط بازخورد جلوی ما باز است.
همین حالا یک اتاق رایگان بسازید — کل سطح عمومی باز است، بدون ثبتنام.