{"openapi":"3.0.3","info":{"title":"API حضور و غیاب پنل آیتی‌لند","version":"1.0.0","description":"این API حضور و غیاب کلاس‌های برگزارشده روی سرورهای BigBlueButton موسسه را به‌صورت **فقط‌خواندنی** و در قالب JSON در اختیار سامانه‌های مجاز قرار می‌دهد.\n\n### شروع کار\n1. کلید API را از مدیر پنل موسسهٔ خود دریافت کنید.\n2. دکمهٔ **Authorize** را بزنید و کلید را وارد کنید.\n3. مسیر \u0060whoami\u0060 را اجرا کنید تا از صحت اتصال مطمئن شوید.\n\n### نکات کلیدی\n- هر کلید فقط به داده‌های موسسهٔ خودش دسترسی دارد.\n- همهٔ زمان‌ها UTC هستند؛ «روز» همیشه به وقت تهران محاسبه می‌شود.\n- آی‌پی و متن چت فقط با \u0060include\u0060 و مجوز کلید بازگردانده می‌شوند.\n- تعداد درخواست در دقیقه برای هر کلید محدود است (پیش‌فرض ۱۲۰).\n\nراهنمای کامل، مفاهیم و نمونه کد: [راهنمای استفاده از API](/api-docs)"},"servers":[{"url":"/","description":"همین سرور"}],"tags":[{"name":"اتصال","description":"بررسی صحت اتصال و کلید."},{"name":"جلسه‌ها","description":"فهرست و جزئیات جلسه‌ها. هر بار تشکیل کلاس یک جلسهٔ مستقل است."},{"name":"کلاس‌ها","description":"دسترسی بر اساس کلاس (\u0060externalMeetingId\u0060): جلسه‌های کلاس و جمع‌بندی حضور هر فرد."}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"externalDocs":{"description":"راهنمای کامل استفاده از API","url":"/api-docs"},"paths":{"/api/v1/whoami":{"get":{"tags":["اتصال"],"operationId":"whoami","summary":"بررسی کلید و مجوزها","description":"برای اطمینان از صحت اتصال و اعتبار کلید استفاده می‌شود؛ نام موسسه، نام کلید، مجوزهای \u0060include\u0060 و زمان سرور را بازمی‌گرداند.\n\nتوصیه می‌شود نخستین فراخوانی پس از دریافت کلید، همین مسیر باشد.","parameters":[],"responses":{"200":{"description":"کلید معتبر است.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiWhoAmI"},"examples":{"نمونه":{"summary":"پاسخ موفق","value":{"institute":"دانشگاه نمونه","keyName":"سامانهٔ آموزش دانشگاه","keyPrefix":"itl_AbC12x","canViewIpAddresses":false,"canViewChat":false,"expiresAt":null,"serverTime":"2026-09-20T14:30:00\u002B00:00"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/api/v1/meetings":{"get":{"tags":["جلسه‌ها"],"operationId":"listMeetings","summary":"فهرست جلسه‌ها","description":"جلسه‌های موسسه را از تازه‌ترین به قدیمی‌ترین برمی‌گرداند.\n\n- هر بار تشکیل کلاس یک **جلسهٔ مستقل** است؛ کلاسی که چند بار در روز برگزار شده باشد چند ردیف جدا دارد.\n- پاسخ فقط خلاصهٔ جلسه‌هاست (بدون شرکت‌کنندگان). برای دریافت حاضرین از «جزئیات جلسه» یا «جلسه‌های یک کلاس» استفاده کنید.\n- فیلترها با هم ترکیب می‌شوند (AND). بازهٔ زمانی بر اساس **زمان شروع جلسه** اعمال می‌شود.","parameters":[{"$ref":"#/components/parameters/ExternalMeetingIdQuery"},{"$ref":"#/components/parameters/ExternalMeetingIdPrefixQuery"},{"$ref":"#/components/parameters/CourseCodeQuery"},{"$ref":"#/components/parameters/StatusQuery"},{"$ref":"#/components/parameters/Date"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"$ref":"#/components/parameters/Page"},{"name":"pageSize","in":"query","description":"تعداد ردیف در هر صفحه (۱ تا 100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"فهرست جلسه‌ها (صفحه‌بندی‌شده).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiPageOfApiSessionSummary"},"examples":{"نمونه":{"summary":"دو جلسهٔ یک کلاس در یک روز","value":{"items":[{"sessionId":"c3d81f52-7e4b-4a19-8f60-1b2a9d5e7c44","internalMeetingId":"c3d81f527e4b4a198f601b2a9d5e7c44c3d81f52-1789909200000","externalMeetingId":"class42","name":"ریاضی ۱ - جلسهٔ عصر","course":{"code":"MATH101","name":"ریاضی ۱"},"origin":{"system":"Moodle","version":"4.5.5","serverName":"lms.example.edu","courseId":"42","courseName":"ریاضی ۱","courseLabel":"math-101"},"status":"ended","startedAt":"2026-09-20T13:00:00\u002B00:00","endedAt":"2026-09-20T14:00:00\u002B00:00","durationSeconds":3600,"localDate":"2026-09-20","participantCount":3,"chatMessageCount":0},{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","internalMeetingId":"5b1e0f3c0f7a4b539c1e2d8a4d6f9a105b1e0f3c-1789880400000","externalMeetingId":"class42","name":"ریاضی ۱ - جلسهٔ صبح","course":{"code":"MATH101","name":"ریاضی ۱"},"origin":{"system":"Moodle","version":"4.5.5","serverName":"lms.example.edu","courseId":"42","courseName":"ریاضی ۱","courseLabel":"math-101"},"status":"ended","startedAt":"2026-09-20T05:00:00\u002B00:00","endedAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400,"localDate":"2026-09-20","participantCount":3,"chatMessageCount":3}],"page":1,"pageSize":20,"totalCount":2,"totalPages":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/api/v1/meetings/{id}":{"get":{"tags":["جلسه‌ها"],"operationId":"getMeeting","summary":"جزئیات یک جلسه","description":"اطلاعات کامل یک جلسه شامل حاضرین، تمام بازه‌های ورود و خروج، مدت واقعی حضور و درصد حضور هر فرد.\n\n- شناسه می‌تواند \u0060sessionId\u0060 (UUID) یا \u0060internalMeetingId\u0060 بیگ‌بلو باشد.\n- اگر جلسه متعلق به موسسهٔ دیگری باشد، به‌جای ۴۰۳ پاسخ ۴۰۴ برمی‌گردد.\n- آی‌پی و متن چت فقط با \u0060include\u0060 و در صورت داشتن مجوز کلید بازگردانده می‌شوند.","parameters":[{"name":"id","in":"path","required":true,"description":"شناسهٔ جلسه: \u0060sessionId\u0060 (UUID) یا \u0060internalMeetingId\u0060 بیگ‌بلو.","schema":{"type":"string"}},{"$ref":"#/components/parameters/Threshold"},{"$ref":"#/components/parameters/Include"}],"responses":{"200":{"description":"جزئیات جلسه.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiSessionDetail"},"examples":{"basic":{"summary":"پاسخ پیش‌فرض (بدون آی‌پی و چت)","value":{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","internalMeetingId":"5b1e0f3c0f7a4b539c1e2d8a4d6f9a105b1e0f3c-1789880400000","externalMeetingId":"class42","name":"ریاضی ۱ - جلسهٔ صبح","course":{"code":"MATH101","name":"ریاضی ۱"},"origin":{"system":"Moodle","version":"4.5.5","serverName":"lms.example.edu","courseId":"42","courseName":"ریاضی ۱","courseLabel":"math-101"},"status":"ended","startedAt":"2026-09-20T05:00:00\u002B00:00","endedAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400,"localDate":"2026-09-20","localDateJalali":"1405/06/29","sequenceInDay":1,"sessionsInDay":2,"thresholdPercent":70,"baseline":{"externalUserId":"t-100","name":"استاد احمدی","presenceSeconds":5400},"participantCount":3,"belowThresholdCount":1,"peakConcurrentParticipants":3,"isShortSession":false,"deviceSummary":{"mobile":1,"desktop":2,"unknown":0},"recording":{"enabled":true,"recordedSeconds":5280,"isRecording":false},"screenshareCount":1,"presentationChangeCount":3,"maxUsers":0,"plannedDurationMinutes":0,"participants":[{"externalUserId":"t-100","name":"استاد احمدی","role":"moderator","firstJoinedAt":"2026-09-20T05:00:00\u002B00:00","lastLeftAt":"2026-09-20T06:30:00\u002B00:00","isPresent":false,"totalPresenceSeconds":5400,"attendancePercent":100,"belowThreshold":false,"joinCount":1,"chatMessageCount":1,"lateSeconds":0,"earlyLeaveSeconds":0,"activity":{"audioSeconds":5400,"audioDrops":0,"unmuteCount":1,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":1,"wasPresenter":true},"intervals":[{"joinedAt":"2026-09-20T05:00:00\u002B00:00","leftAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400}]},{"externalUserId":"s-2002","name":"علی محمدی","role":"viewer","firstJoinedAt":"2026-09-20T05:00:00\u002B00:00","lastLeftAt":"2026-09-20T06:20:00\u002B00:00","isPresent":false,"totalPresenceSeconds":4800,"attendancePercent":88.9,"belowThreshold":false,"joinCount":1,"chatMessageCount":1,"lateSeconds":0,"earlyLeaveSeconds":600,"activity":{"audioSeconds":0,"audioDrops":0,"unmuteCount":0,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T05:00:00\u002B00:00","leftAt":"2026-09-20T06:20:00\u002B00:00","durationSeconds":4800}]},{"externalUserId":"s-2001","name":"سارا رضایی","role":"viewer","firstJoinedAt":"2026-09-20T05:02:00\u002B00:00","lastLeftAt":"2026-09-20T05:40:00\u002B00:00","isPresent":false,"totalPresenceSeconds":2160,"attendancePercent":40,"belowThreshold":true,"joinCount":2,"chatMessageCount":1,"lateSeconds":120,"earlyLeaveSeconds":3000,"activity":{"audioSeconds":3360,"audioDrops":1,"unmuteCount":1,"cameraSeconds":2100,"cameraStarts":1,"handRaises":1,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T05:02:00\u002B00:00","leftAt":"2026-09-20T05:20:00\u002B00:00","durationSeconds":1080},{"joinedAt":"2026-09-20T05:22:00\u002B00:00","leftAt":"2026-09-20T05:40:00\u002B00:00","durationSeconds":1080}]}]}},"withIpAndChat":{"summary":"با include=ip,chat (نیازمند مجوز کلید)","value":{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","internalMeetingId":"5b1e0f3c0f7a4b539c1e2d8a4d6f9a105b1e0f3c-1789880400000","externalMeetingId":"class42","name":"ریاضی ۱ - جلسهٔ صبح","course":{"code":"MATH101","name":"ریاضی ۱"},"origin":{"system":"Moodle","version":"4.5.5","serverName":"lms.example.edu","courseId":"42","courseName":"ریاضی ۱","courseLabel":"math-101"},"status":"ended","startedAt":"2026-09-20T05:00:00\u002B00:00","endedAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400,"localDate":"2026-09-20","localDateJalali":"1405/06/29","sequenceInDay":1,"sessionsInDay":2,"thresholdPercent":70,"baseline":{"externalUserId":"t-100","name":"استاد احمدی","presenceSeconds":5400},"participantCount":3,"belowThresholdCount":1,"peakConcurrentParticipants":3,"isShortSession":false,"deviceSummary":{"mobile":1,"desktop":2,"unknown":0},"recording":{"enabled":true,"recordedSeconds":5280,"isRecording":false},"screenshareCount":1,"presentationChangeCount":3,"maxUsers":0,"plannedDurationMinutes":0,"participants":[{"externalUserId":"t-100","name":"استاد احمدی","role":"moderator","firstJoinedAt":"2026-09-20T05:00:00\u002B00:00","lastLeftAt":"2026-09-20T06:30:00\u002B00:00","isPresent":false,"totalPresenceSeconds":5400,"attendancePercent":100,"belowThreshold":false,"joinCount":1,"chatMessageCount":1,"lateSeconds":0,"earlyLeaveSeconds":0,"activity":{"audioSeconds":5400,"audioDrops":0,"unmuteCount":1,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":1,"wasPresenter":true},"intervals":[{"joinedAt":"2026-09-20T05:00:00\u002B00:00","leftAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400,"ipAddress":"185.141.104.10","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"}],"ipAddresses":["185.141.104.10"],"device":{"browser":"Chrome 131","os":"Windows","kind":"desktop"}},{"externalUserId":"s-2002","name":"علی محمدی","role":"viewer","firstJoinedAt":"2026-09-20T05:00:00\u002B00:00","lastLeftAt":"2026-09-20T06:20:00\u002B00:00","isPresent":false,"totalPresenceSeconds":4800,"attendancePercent":88.9,"belowThreshold":false,"joinCount":1,"chatMessageCount":1,"lateSeconds":0,"earlyLeaveSeconds":600,"activity":{"audioSeconds":0,"audioDrops":0,"unmuteCount":0,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T05:00:00\u002B00:00","leftAt":"2026-09-20T06:20:00\u002B00:00","durationSeconds":4800,"ipAddress":"37.98.4.4","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0"}],"ipAddresses":["37.98.4.4"],"device":{"browser":"Firefox 130","os":"Windows","kind":"desktop"}},{"externalUserId":"s-2001","name":"سارا رضایی","role":"viewer","firstJoinedAt":"2026-09-20T05:02:00\u002B00:00","lastLeftAt":"2026-09-20T05:40:00\u002B00:00","isPresent":false,"totalPresenceSeconds":2160,"attendancePercent":40,"belowThreshold":true,"joinCount":2,"chatMessageCount":1,"lateSeconds":120,"earlyLeaveSeconds":3000,"activity":{"audioSeconds":3360,"audioDrops":1,"unmuteCount":1,"cameraSeconds":2100,"cameraStarts":1,"handRaises":1,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T05:02:00\u002B00:00","leftAt":"2026-09-20T05:20:00\u002B00:00","durationSeconds":1080,"ipAddress":"5.22.10.7","userAgent":"Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36"},{"joinedAt":"2026-09-20T05:22:00\u002B00:00","leftAt":"2026-09-20T05:40:00\u002B00:00","durationSeconds":1080,"ipAddress":"5.22.10.8","userAgent":"Mozilla/5.0 (Linux; Android 14; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36"}],"ipAddresses":["5.22.10.7","5.22.10.8"],"device":{"browser":"Chrome 131","os":"Android","kind":"mobile"}}],"chat":[{"senderName":"سارا رضایی","externalUserId":"s-2001","message":"سلام استاد، صدا میاد؟","chatId":"MAIN-PUBLIC-GROUP-CHAT","isPrivate":false,"sentAt":"2026-09-20T05:03:00\u002B00:00"},{"senderName":"استاد احمدی","externalUserId":"t-100","message":"بله، شروع می‌کنیم.","chatId":"MAIN-PUBLIC-GROUP-CHAT","isPrivate":false,"sentAt":"2026-09-20T05:04:00\u002B00:00"},{"senderName":"علی محمدی","externalUserId":"s-2002","message":"یک سؤال خصوصی دارم.","chatId":"b7e1c0-private-chat","isPrivate":true,"sentAt":"2026-09-20T05:30:00\u002B00:00"}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/api/v1/classes/{externalMeetingId}/sessions":{"get":{"tags":["کلاس‌ها"],"operationId":"listClassSessions","summary":"جلسه‌های یک کلاس","description":"همهٔ جلسه‌های یک کلاس (بر اساس \u0060externalMeetingId\u0060) را با جزئیات کامل و به ترتیب زمان برمی‌گرداند.\n\n- کلاسی که در یک روز دو بار تشکیل شده باشد، **دو جلسهٔ جدا** با شناسه‌های متفاوت است؛ فیلدهای \u0060sequenceInDay\u0060 و \u0060sessionsInDay\u0060 شمارهٔ جلسه در آن روز را مشخص می‌کنند.\n- ارسال \u0060date\u0060 یا \u0060from\u0060 **الزامی** است.\n- اگر هیچ جلسه‌ای در بازه نباشد، پاسخ ۲۰۰ با فهرست خالی است؛ ۴۰۴ فقط وقتی برمی‌گردد که این شناسهٔ کلاس اصلاً برای موسسه ثبت نشده باشد.","parameters":[{"name":"externalMeetingId","in":"path","required":true,"description":"شناسهٔ کلاس در سامانهٔ شما (همان \u0060meetingID\u0060 که هنگام ساخت کلاس به BigBlueButton داده شده است).","schema":{"type":"string"}},{"$ref":"#/components/parameters/Date"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"$ref":"#/components/parameters/Threshold"},{"$ref":"#/components/parameters/Include"},{"$ref":"#/components/parameters/Page"},{"name":"pageSize","in":"query","description":"تعداد ردیف در هر صفحه (۱ تا 50).","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"جلسه‌های کلاس با جزئیات.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiPageOfApiSessionDetail"},"examples":{"نمونه":{"summary":"دو جلسهٔ یک روز","value":{"items":[{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","internalMeetingId":"5b1e0f3c0f7a4b539c1e2d8a4d6f9a105b1e0f3c-1789880400000","externalMeetingId":"class42","name":"ریاضی ۱ - جلسهٔ صبح","course":{"code":"MATH101","name":"ریاضی ۱"},"origin":{"system":"Moodle","version":"4.5.5","serverName":"lms.example.edu","courseId":"42","courseName":"ریاضی ۱","courseLabel":"math-101"},"status":"ended","startedAt":"2026-09-20T05:00:00\u002B00:00","endedAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400,"localDate":"2026-09-20","localDateJalali":"1405/06/29","sequenceInDay":1,"sessionsInDay":2,"thresholdPercent":70,"baseline":{"externalUserId":"t-100","name":"استاد احمدی","presenceSeconds":5400},"participantCount":3,"belowThresholdCount":1,"peakConcurrentParticipants":3,"isShortSession":false,"deviceSummary":{"mobile":1,"desktop":2,"unknown":0},"recording":{"enabled":true,"recordedSeconds":5280,"isRecording":false},"screenshareCount":1,"presentationChangeCount":3,"maxUsers":0,"plannedDurationMinutes":0,"participants":[{"externalUserId":"t-100","name":"استاد احمدی","role":"moderator","firstJoinedAt":"2026-09-20T05:00:00\u002B00:00","lastLeftAt":"2026-09-20T06:30:00\u002B00:00","isPresent":false,"totalPresenceSeconds":5400,"attendancePercent":100,"belowThreshold":false,"joinCount":1,"chatMessageCount":1,"lateSeconds":0,"earlyLeaveSeconds":0,"activity":{"audioSeconds":5400,"audioDrops":0,"unmuteCount":1,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":1,"wasPresenter":true},"intervals":[{"joinedAt":"2026-09-20T05:00:00\u002B00:00","leftAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400}]},{"externalUserId":"s-2002","name":"علی محمدی","role":"viewer","firstJoinedAt":"2026-09-20T05:00:00\u002B00:00","lastLeftAt":"2026-09-20T06:20:00\u002B00:00","isPresent":false,"totalPresenceSeconds":4800,"attendancePercent":88.9,"belowThreshold":false,"joinCount":1,"chatMessageCount":1,"lateSeconds":0,"earlyLeaveSeconds":600,"activity":{"audioSeconds":0,"audioDrops":0,"unmuteCount":0,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T05:00:00\u002B00:00","leftAt":"2026-09-20T06:20:00\u002B00:00","durationSeconds":4800}]},{"externalUserId":"s-2001","name":"سارا رضایی","role":"viewer","firstJoinedAt":"2026-09-20T05:02:00\u002B00:00","lastLeftAt":"2026-09-20T05:40:00\u002B00:00","isPresent":false,"totalPresenceSeconds":2160,"attendancePercent":40,"belowThreshold":true,"joinCount":2,"chatMessageCount":1,"lateSeconds":120,"earlyLeaveSeconds":3000,"activity":{"audioSeconds":3360,"audioDrops":1,"unmuteCount":1,"cameraSeconds":2100,"cameraStarts":1,"handRaises":1,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T05:02:00\u002B00:00","leftAt":"2026-09-20T05:20:00\u002B00:00","durationSeconds":1080},{"joinedAt":"2026-09-20T05:22:00\u002B00:00","leftAt":"2026-09-20T05:40:00\u002B00:00","durationSeconds":1080}]}]},{"sessionId":"c3d81f52-7e4b-4a19-8f60-1b2a9d5e7c44","internalMeetingId":"c3d81f527e4b4a198f601b2a9d5e7c44c3d81f52-1789909200000","externalMeetingId":"class42","name":"ریاضی ۱ - جلسهٔ عصر","course":{"code":"MATH101","name":"ریاضی ۱"},"origin":{"system":"Moodle","version":"4.5.5","serverName":"lms.example.edu","courseId":"42","courseName":"ریاضی ۱","courseLabel":"math-101"},"status":"ended","startedAt":"2026-09-20T13:00:00\u002B00:00","endedAt":"2026-09-20T14:00:00\u002B00:00","durationSeconds":3600,"localDate":"2026-09-20","localDateJalali":"1405/06/29","sequenceInDay":2,"sessionsInDay":2,"thresholdPercent":70,"baseline":{"externalUserId":"t-100","name":"استاد احمدی","presenceSeconds":3600},"participantCount":3,"belowThresholdCount":1,"peakConcurrentParticipants":3,"isShortSession":false,"deviceSummary":{"mobile":1,"desktop":2,"unknown":0},"recording":{"enabled":true,"recordedSeconds":3480,"isRecording":false},"screenshareCount":1,"presentationChangeCount":3,"maxUsers":0,"plannedDurationMinutes":0,"participants":[{"externalUserId":"t-100","name":"استاد احمدی","role":"moderator","firstJoinedAt":"2026-09-20T13:00:00\u002B00:00","lastLeftAt":"2026-09-20T14:00:00\u002B00:00","isPresent":false,"totalPresenceSeconds":3600,"attendancePercent":100,"belowThreshold":false,"joinCount":1,"chatMessageCount":0,"lateSeconds":0,"earlyLeaveSeconds":0,"activity":{"audioSeconds":0,"audioDrops":0,"unmuteCount":0,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T13:00:00\u002B00:00","leftAt":"2026-09-20T14:00:00\u002B00:00","durationSeconds":3600}]},{"externalUserId":"s-2001","name":"سارا رضایی","role":"viewer","firstJoinedAt":"2026-09-20T13:00:00\u002B00:00","lastLeftAt":"2026-09-20T13:30:00\u002B00:00","isPresent":false,"totalPresenceSeconds":1800,"attendancePercent":50,"belowThreshold":true,"joinCount":1,"chatMessageCount":0,"lateSeconds":0,"earlyLeaveSeconds":1800,"activity":{"audioSeconds":0,"audioDrops":0,"unmuteCount":0,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T13:00:00\u002B00:00","leftAt":"2026-09-20T13:30:00\u002B00:00","durationSeconds":1800}]},{"externalUserId":"s-2003","name":"نیما کاظمی","role":"viewer","firstJoinedAt":"2026-09-20T13:05:00\u002B00:00","lastLeftAt":"2026-09-20T13:58:00\u002B00:00","isPresent":false,"totalPresenceSeconds":3180,"attendancePercent":88.3,"belowThreshold":false,"joinCount":1,"chatMessageCount":0,"lateSeconds":300,"earlyLeaveSeconds":120,"activity":{"audioSeconds":0,"audioDrops":0,"unmuteCount":0,"cameraSeconds":0,"cameraStarts":0,"handRaises":0,"screenshares":0,"wasPresenter":false},"intervals":[{"joinedAt":"2026-09-20T13:05:00\u002B00:00","leftAt":"2026-09-20T13:58:00\u002B00:00","durationSeconds":3180}]}]}],"page":1,"pageSize":20,"totalCount":2,"totalPages":1}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/api/v1/classes/{externalMeetingId}/attendance":{"get":{"tags":["کلاس‌ها"],"operationId":"getClassAttendance","summary":"جمع‌بندی حضور کلاس","description":"جمع‌بندی حضور هر فرد در همهٔ جلسه‌های یک کلاس در بازهٔ زمانی: تعداد جلسه‌های حاضر و غایب، مجموع مدت حضور و میانگین درصد حضور.\n\n- برای گزارش پایان ترم، \u0060from\u0060 و \u0060to\u0060 را روی کل ترم تنظیم کنید. سقف: ۵۰۰ جلسه در هر درخواست.\n- «غایب» یعنی کاربر در آن جلسه اصلاً وارد نشده است؛ میانگین روی **همهٔ** جلسه‌های بازه محاسبه می‌شود (غیبت = ۰٪).\n- فقط افرادی فهرست می‌شوند که دست‌کم یک‌بار وارد جلسه شده‌اند. فهرست ثبت‌نام‌شدگان در اختیار پنل نیست و باید سامانهٔ شما آن را با این خروجی تطبیق دهد.\n- ارسال \u0060date\u0060 یا \u0060from\u0060 **الزامی** است.","parameters":[{"name":"externalMeetingId","in":"path","required":true,"description":"شناسهٔ کلاس در سامانهٔ شما (همان \u0060meetingID\u0060 که هنگام ساخت کلاس به BigBlueButton داده شده است).","schema":{"type":"string"}},{"$ref":"#/components/parameters/Date"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"$ref":"#/components/parameters/Threshold"}],"responses":{"200":{"description":"جمع‌بندی حضور افراد.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiClassAttendance"},"examples":{"نمونه":{"summary":"دو جلسه در یک روز","value":{"externalMeetingId":"class42","from":"2026-09-19T20:30:00\u002B00:00","to":"2026-09-20T20:30:00\u002B00:00","thresholdPercent":70,"sessionCount":2,"sessions":[{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","localDate":"2026-09-20","sequenceInDay":1,"startedAt":"2026-09-20T05:00:00\u002B00:00","endedAt":"2026-09-20T06:30:00\u002B00:00","durationSeconds":5400,"baselineSeconds":5400,"participantCount":3},{"sessionId":"c3d81f52-7e4b-4a19-8f60-1b2a9d5e7c44","localDate":"2026-09-20","sequenceInDay":2,"startedAt":"2026-09-20T13:00:00\u002B00:00","endedAt":"2026-09-20T14:00:00\u002B00:00","durationSeconds":3600,"baselineSeconds":3600,"participantCount":3}],"participants":[{"externalUserId":"t-100","name":"استاد احمدی","role":"moderator","sessionsAttended":2,"sessionsAbsent":0,"sessionsBelowThreshold":0,"totalPresenceSeconds":9000,"averagePercent":100,"sessions":[{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","presenceSeconds":5400,"attendancePercent":100,"belowThreshold":false,"joinCount":1},{"sessionId":"c3d81f52-7e4b-4a19-8f60-1b2a9d5e7c44","presenceSeconds":3600,"attendancePercent":100,"belowThreshold":false,"joinCount":1}]},{"externalUserId":"s-2001","name":"سارا رضایی","role":"viewer","sessionsAttended":2,"sessionsAbsent":0,"sessionsBelowThreshold":2,"totalPresenceSeconds":3960,"averagePercent":45,"sessions":[{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","presenceSeconds":2160,"attendancePercent":40,"belowThreshold":true,"joinCount":2},{"sessionId":"c3d81f52-7e4b-4a19-8f60-1b2a9d5e7c44","presenceSeconds":1800,"attendancePercent":50,"belowThreshold":true,"joinCount":1}]},{"externalUserId":"s-2002","name":"علی محمدی","role":"viewer","sessionsAttended":1,"sessionsAbsent":1,"sessionsBelowThreshold":0,"totalPresenceSeconds":4800,"averagePercent":44.4,"sessions":[{"sessionId":"5b1e0f3c-0f7a-4b53-9c1e-2d8a4d6f9a10","presenceSeconds":4800,"attendancePercent":88.9,"belowThreshold":false,"joinCount":1}]},{"externalUserId":"s-2003","name":"نیما کاظمی","role":"viewer","sessionsAttended":1,"sessionsAbsent":1,"sessionsBelowThreshold":0,"totalPresenceSeconds":3180,"averagePercent":44.2,"sessions":[{"sessionId":"c3d81f52-7e4b-4a19-8f60-1b2a9d5e7c44","presenceSeconds":3180,"attendancePercent":88.3,"belowThreshold":false,"joinCount":1}]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}}},"components":{"schemas":{"ApiWhoAmI":{"type":"object","description":"هویت کلید و مجوزهای آن.","required":["institute","keyName","keyPrefix","canViewIpAddresses","canViewChat","expiresAt","serverTime"],"properties":{"institute":{"type":"string","description":"نام موسسه‌ای که کلید به آن تعلق دارد."},"keyName":{"type":"string","description":"نامی که مدیر پنل هنگام ساخت کلید تعیین کرده است."},"keyPrefix":{"type":"string","description":"ابتدای کلید برای شناسایی در پنل مدیریت. کلید کامل هرگز بازگردانده نمی‌شود."},"canViewIpAddresses":{"type":"boolean","description":"آیا کلید مجاز به دریافت آی‌پی و مرورگر شرکت‌کنندگان (include=ip) است."},"canViewChat":{"type":"boolean","description":"آیا کلید مجاز به دریافت متن پیام‌های چت (include=chat) است."},"expiresAt":{"type":"string","format":"date-time","nullable":true,"description":"زمان انقضای کلید (UTC). مقدار null یعنی کلید انقضا ندارد."},"serverTime":{"type":"string","format":"date-time","description":"زمان فعلی سرور (UTC)؛ برای بررسی همگام‌بودن ساعت سامانهٔ شما."}}},"ApiPageOfApiSessionSummary":{"type":"object","description":"پوشش همهٔ فهرست‌های صفحه‌بندی‌شده.","required":["items","page","pageSize","totalCount","totalPages"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ApiSessionSummary"},"description":"نتایج همین صفحه."},"page":{"type":"integer","format":"int32","description":"شمارهٔ صفحهٔ جاری (از ۱)."},"pageSize":{"type":"integer","format":"int32","description":"حداکثر تعداد ردیف در هر صفحه."},"totalCount":{"type":"integer","format":"int32","description":"تعداد کل نتایج منطبق با فیلترها، در تمام صفحات."},"totalPages":{"type":"integer","format":"int32","description":"تعداد کل صفحات."}}},"ApiSessionSummary":{"type":"object","description":"خلاصهٔ یک جلسه (یک بار تشکیل کلاس)، بدون فهرست شرکت‌کنندگان.","required":["sessionId","internalMeetingId","externalMeetingId","name","course","origin","status","startedAt","endedAt","durationSeconds","localDate","participantCount","chatMessageCount"],"properties":{"sessionId":{"type":"string","format":"uuid","description":"شناسهٔ یکتای جلسه در پنل (UUID)؛ برای دریافت جزئیات جلسه استفاده می‌شود."},"internalMeetingId":{"type":"string","description":"شناسهٔ داخلی جلسه در BigBlueButton؛ به‌ازای هر بار تشکیل کلاس یکتاست."},"externalMeetingId":{"type":"string","description":"شناسه‌ای که سامانهٔ شما هنگام ساخت کلاس به BigBlueButton داده است (meetingID). ممکن است بین چند جلسه مشترک باشد."},"name":{"type":"string","description":"عنوان جلسه."},"course":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiCourseRef"}],"description":"درس متناظر، در صورتی که در پنل تطبیق داده شده باشد؛ در غیر این صورت null."},"origin":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiOrigin"}],"description":"سامانهٔ مبدأ کلاس؛ اگر سامانه metadata نفرستاده یا پنل ساخته‌شدن کلاس را ندیده باشد null."},"status":{"type":"string","description":"وضعیت جلسه: live (در حال برگزاری) یا ended (پایان‌یافته).","enum":["live","ended"]},"startedAt":{"type":"string","format":"date-time","description":"زمان شروع جلسه (UTC)."},"endedAt":{"type":"string","format":"date-time","nullable":true,"description":"زمان پایان جلسه (UTC)؛ برای جلسهٔ در حال برگزاری null است."},"durationSeconds":{"type":"integer","format":"int64","description":"مدت جلسه به ثانیه؛ برای جلسهٔ در حال برگزاری تا لحظهٔ درخواست حساب می‌شود."},"localDate":{"type":"string","description":"تاریخ میلادی روز برگزاری به وقت تهران (yyyy-MM-dd).","format":"date"},"participantCount":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگان ثبت‌شده در جلسه."},"chatMessageCount":{"type":"integer","format":"int32","description":"تعداد پیام‌های چت ثبت‌شده در جلسه."}}},"ApiCourseRef":{"type":"object","description":"درس تعریف‌شده در پنل که این جلسه به آن تطبیق داده شده است.","required":["code","name"],"properties":{"code":{"type":"string","nullable":true,"description":"کد درس تعریف‌شده در پنل؛ ممکن است خالی (null) باشد."},"name":{"type":"string","description":"نام درس یا دوره."}}},"ApiOrigin":{"type":"object","description":"سامانه‌ای (مثلاً مودل) که این کلاس را ساخته؛ از metadata که همان سامانه هنگام ساخت کلاس به بیگ‌بلو باتن فرستاده است.","required":["system","version","serverName","courseId","courseName","courseLabel"],"properties":{"system":{"type":"string","nullable":true,"description":"نوع سامانهٔ مبدأ، مثلاً Moodle؛ در صورت‌نفرستادن null."},"version":{"type":"string","nullable":true,"description":"نسخهٔ سامانهٔ مبدأ؛ در صورت‌نفرستادن null."},"serverName":{"type":"string","nullable":true,"description":"آدرس/نام سامانهٔ مبدأ، مثلاً lms.example.edu؛ در صورت‌نفرستادن null."},"courseId":{"type":"string","nullable":true,"description":"شناسهٔ درس در سامانهٔ مبدأ (مثلاً شناسهٔ course در مودل)."},"courseName":{"type":"string","nullable":true,"description":"نام درس در سامانهٔ مبدأ."},"courseLabel":{"type":"string","nullable":true,"description":"برچسب/نام کوتاه درس در سامانهٔ مبدأ."}}},"ApiSessionDetail":{"type":"object","description":"جزئیات کامل یک جلسه شامل حاضرین، بازه‌های ورود و خروج و درصد حضور.","required":["sessionId","internalMeetingId","externalMeetingId","name","course","origin","status","startedAt","endedAt","durationSeconds","localDate","localDateJalali","sequenceInDay","sessionsInDay","thresholdPercent","baseline","participantCount","belowThresholdCount","peakConcurrentParticipants","isShortSession","deviceSummary","recording","screenshareCount","presentationChangeCount","maxUsers","plannedDurationMinutes","participants"],"properties":{"sessionId":{"type":"string","format":"uuid","description":"شناسهٔ یکتای جلسه در پنل (UUID)."},"internalMeetingId":{"type":"string","description":"شناسهٔ داخلی جلسه در BigBlueButton؛ به‌ازای هر بار تشکیل کلاس یکتاست."},"externalMeetingId":{"type":"string","description":"شناسه‌ای که سامانهٔ شما هنگام ساخت کلاس داده است (meetingID). ممکن است بین چند جلسه مشترک باشد."},"name":{"type":"string","description":"عنوان جلسه."},"course":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiCourseRef"}],"description":"درس متناظر در پنل؛ در صورت نبودن null."},"origin":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiOrigin"}],"description":"سامانهٔ مبدأ کلاس؛ اگر سامانه metadata نفرستاده یا پنل ساخته‌شدن کلاس را ندیده باشد null."},"status":{"type":"string","description":"وضعیت جلسه: live یا ended.","enum":["live","ended"]},"startedAt":{"type":"string","format":"date-time","description":"زمان شروع جلسه (UTC)."},"endedAt":{"type":"string","format":"date-time","nullable":true,"description":"زمان پایان جلسه (UTC)؛ برای جلسهٔ در حال برگزاری null."},"durationSeconds":{"type":"integer","format":"int64","description":"مدت جلسه به ثانیه."},"localDate":{"type":"string","description":"تاریخ میلادی روز برگزاری به وقت تهران (yyyy-MM-dd).","format":"date"},"localDateJalali":{"type":"string","description":"تاریخ شمسی روز برگزاری به وقت تهران (yyyy/MM/dd)."},"sequenceInDay":{"type":"integer","format":"int32","description":"شمارهٔ این جلسه میان جلسه‌های همان کلاس در همان روز تهرانی (از ۱)، به ترتیب زمان شروع."},"sessionsInDay":{"type":"integer","format":"int32","description":"تعداد کل جلسه‌های همان کلاس در همان روز تهرانی."},"thresholdPercent":{"type":"integer","format":"int32","description":"آستانهٔ درصد حضور که در این پاسخ اعمال شده است."},"baseline":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiBaseline"}],"description":"مبنای محاسبهٔ درصد؛ اگر جلسه شرکت‌کننده‌ای نداشته باشد null."},"participantCount":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگان جلسه."},"belowThresholdCount":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگانی که درصد حضورشان کمتر از آستانه است."},"peakConcurrentParticipants":{"type":"integer","format":"int32","description":"بیشینهٔ تعداد افراد هم‌زمان حاضر در جلسه؛ می‌تواند از participantCount کمتر باشد."},"isShortSession":{"type":"boolean","description":"true اگر جلسه پایان یافته و کمتر از ۵ دقیقه طول کشیده باشد (معمولاً جلسهٔ آزمایشی یا اشتباهی)."},"deviceSummary":{"allOf":[{"$ref":"#/components/schemas/ApiDeviceSummary"}],"description":"تعداد شرکت‌کنندگان به تفکیک نوع دستگاه اصلی؛ بدون نیاز به include=ip."},"recording":{"allOf":[{"$ref":"#/components/schemas/ApiRecordingInfo"}],"description":"وضعیت ضبط کلاس."},"screenshareCount":{"type":"integer","format":"int32","description":"تعداد دفعات اشتراک صفحه در کلاس."},"presentationChangeCount":{"type":"integer","format":"int32","description":"تعداد دفعات تغییر ارائه/اسلاید فعال کلاس."},"maxUsers":{"type":"integer","format":"int32","nullable":true,"description":"سقف تعداد کاربران که سامانهٔ مبدأ تعیین کرده؛ ۰ یعنی نامحدود و null یعنی اطلاعاتی نیست."},"plannedDurationMinutes":{"type":"integer","format":"int32","nullable":true,"description":"سقف مدت کلاس به دقیقه که سامانهٔ مبدأ تعیین کرده؛ ۰ یعنی نامحدود و null یعنی اطلاعاتی نیست."},"participants":{"type":"array","items":{"$ref":"#/components/schemas/ApiParticipant"},"description":"شرکت‌کنندگان به ترتیب زمان نخستین ورود."},"chat":{"type":"array","items":{"$ref":"#/components/schemas/ApiChatMessage"},"nullable":true,"description":"پیام‌های چت به ترتیب زمان. فقط با include=chat و مجوز کلید."},"duplicateNameGroups":{"type":"array","items":{"$ref":"#/components/schemas/ApiDuplicateParticipantGroup"},"nullable":true,"description":"هشدار احتمال شکستگی حضور یک نفر بین چند ردیف participants (وقتی سامانهٔ شما هنگام join یک userID پایدار نمی‌دهد). فقط وقتی در همین جلسه دو یا چند شرکت‌کننده نام یکسان دارند پر می‌شود؛ در غیر این صورت null. مجموع واقعی حضور این افراد را از combinedPresenceSeconds بخوانید."}}},"ApiBaseline":{"type":"object","description":"مبنای محاسبهٔ درصد حضور: پرحضورترین فرد همان جلسه (معمولاً مدرس).","required":["externalUserId","name","presenceSeconds"],"properties":{"externalUserId":{"type":"string","description":"شناسهٔ کاربر پرحضورترین فرد جلسه."},"name":{"type":"string","description":"نام پرحضورترین فرد جلسه."},"presenceSeconds":{"type":"integer","format":"int64","description":"مدت حضور او به ثانیه (معادل ۱۰۰٪)."}}},"ApiDeviceSummary":{"type":"object","description":"تعداد شرکت‌کنندگان جلسه به تفکیک نوع دستگاه اصلی.","required":["mobile","desktop","unknown"],"properties":{"mobile":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگانی که دستگاه اصلی‌شان موبایل یا تبلت است."},"desktop":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگانی که دستگاه اصلی‌شان رایانه است."},"unknown":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگانی که دستگاهشان تشخیص داده نشد (User-Agent ثبت نشده)."}}},"ApiRecordingInfo":{"type":"object","description":"وضعیت ضبط کلاس.","required":["enabled","recordedSeconds","isRecording"],"properties":{"enabled":{"type":"boolean","description":"آیا کلاس با گزینهٔ ضبط ساخته شده یا ضبط در آن روشن شده است."},"recordedSeconds":{"type":"integer","format":"int64","description":"مجموع ثانیه‌هایی که ضبط روشن بوده است."},"isRecording":{"type":"boolean","description":"آیا ضبط در لحظهٔ درخواست روشن است (فقط برای جلسهٔ در حال برگزاری)."}}},"ApiParticipant":{"type":"object","description":"حضور یک کاربر در یک جلسه.","required":["externalUserId","name","role","firstJoinedAt","lastLeftAt","isPresent","totalPresenceSeconds","attendancePercent","belowThreshold","joinCount","chatMessageCount","lateSeconds","earlyLeaveSeconds","activity","intervals"],"properties":{"externalUserId":{"type":"string","description":"شناسهٔ ثابت کاربر در سامانهٔ شما (userID که هنگام ورود به کلاس ارسال شده است)."},"name":{"type":"string","description":"نام نمایشی کاربر در جلسه."},"role":{"type":"string","description":"نقش کاربر: moderator (مدرس یا مدیر) یا viewer (شرکت‌کننده).","enum":["moderator","viewer"]},"firstJoinedAt":{"type":"string","format":"date-time","description":"زمان نخستین ورود (UTC)."},"lastLeftAt":{"type":"string","format":"date-time","nullable":true,"description":"زمان آخرین خروج (UTC)؛ اگر کاربر هم‌اکنون در جلسه است null."},"isPresent":{"type":"boolean","description":"آیا کاربر در لحظهٔ درخواست در جلسه حضور دارد."},"totalPresenceSeconds":{"type":"integer","format":"int64","description":"مدت واقعی حضور به ثانیه؛ اجتماع همهٔ بازه‌ها. بازه‌های هم‌پوشان یک‌بار شمرده می‌شوند."},"attendancePercent":{"type":"number","format":"double","description":"درصد حضور نسبت به پرحضورترین فرد همان جلسه (معمولاً مدرس = ۱۰۰)، با یک رقم اعشار."},"belowThreshold":{"type":"boolean","description":"true اگر attendancePercent کمتر از threshold باشد."},"joinCount":{"type":"integer","format":"int32","description":"تعداد دفعات ورود (تعداد بازه‌ها)."},"chatMessageCount":{"type":"integer","format":"int32","description":"تعداد پیام‌های چت این کاربر در جلسه."},"lateSeconds":{"type":"integer","format":"int64","description":"تأخیر در ورود به ثانیه: فاصلهٔ نخستین ورود تا شروع جلسه (startedAt). هیچ‌وقت منفی نیست؛ ۰ یعنی از ابتدا حاضر بوده است."},"earlyLeaveSeconds":{"type":"integer","format":"int64","nullable":true,"description":"زودرفتن به ثانیه: فاصلهٔ آخرین خروج تا پایان جلسه (endedAt). برای جلسهٔ در حال برگزاری یا کاربری که هنوز در جلسه است null؛ ۰ یعنی تا پایان مانده است."},"activity":{"allOf":[{"$ref":"#/components/schemas/ApiActivity"}],"description":"فعالیت صدا، دوربین، دست و اشتراک صفحه؛ برای کلاس‌هایی که پیش از فعال‌شدن این قابلیت برگزار شده‌اند صفر است."},"intervals":{"type":"array","items":{"$ref":"#/components/schemas/ApiInterval"},"description":"بازه‌های پیوستهٔ حضور، به ترتیب زمان."},"ipAddresses":{"type":"array","items":{"type":"string"},"nullable":true,"description":"آی‌پی‌های یکتای کاربر در این جلسه. فقط با include=ip و مجوز کلید."},"device":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiDevice"}],"description":"دستگاه اصلی (آخرین ورود). فقط با include=ip و مجوز کلید؛ اگر User-Agent ثبت نشده باشد حذف می‌شود."}}},"ApiActivity":{"type":"object","description":"فعالیت یک شرکت‌کننده در جلسه از رویدادهای صدا، دوربین، دست و اشتراک صفحه.","required":["audioSeconds","audioDrops","unmuteCount","cameraSeconds","cameraStarts","handRaises","screenshares","wasPresenter"],"properties":{"audioSeconds":{"type":"integer","format":"int64","description":"مجموع ثانیه‌هایی که اتصال صدای کاربر برقرار بوده است."},"audioDrops":{"type":"integer","format":"int32","description":"تعداد دفعاتی که اتصال صدا وسط کلاس قطع شده؛ شاخصی از ناپایداری اتصال (خروج عادی از کلاس حساب نمی‌شود)."},"unmuteCount":{"type":"integer","format":"int32","description":"تعداد دفعاتی که میکروفون را باز کرده است."},"cameraSeconds":{"type":"integer","format":"int64","description":"مجموع ثانیه‌هایی که دوربینش روشن بوده است."},"cameraStarts":{"type":"integer","format":"int32","description":"تعداد دفعاتی که دوربین را روشن کرده است."},"handRaises":{"type":"integer","format":"int32","description":"تعداد دفعاتی که دست بلند کرده است."},"screenshares":{"type":"integer","format":"int32","description":"تعداد دفعاتی که صفحه را به اشتراک گذاشته است."},"wasPresenter":{"type":"boolean","description":"آیا در این جلسه نقش ارائه‌دهنده (presenter) گرفته است."}}},"ApiInterval":{"type":"object","description":"یک بازهٔ پیوستهٔ حضور، از ورود تا خروج.","required":["joinedAt","leftAt","durationSeconds"],"properties":{"joinedAt":{"type":"string","format":"date-time","description":"زمان ورود (UTC)."},"leftAt":{"type":"string","format":"date-time","nullable":true,"description":"زمان خروج (UTC)؛ اگر کاربر هنوز در جلسه است null."},"durationSeconds":{"type":"integer","format":"int64","description":"طول این بازه به ثانیه."},"ipAddress":{"type":"string","nullable":true,"description":"آی‌پی اتصال. فقط با include=ip و مجوز کلید بازگردانده می‌شود."},"userAgent":{"type":"string","nullable":true,"description":"مشخصات مرورگر یا دستگاه. فقط با include=ip و مجوز کلید بازگردانده می‌شود."}}},"ApiDevice":{"type":"object","description":"دستگاه اصلی شرکت‌کننده (دستگاه آخرین ورودش) که از User-Agent تشخیص داده شده است. فقط با include=ip.","required":["browser","os","kind"],"properties":{"browser":{"type":"string","nullable":true,"description":"نام و نسخهٔ مرورگر، مثلاً «Chrome 131»؛ در صورت تشخیص‌ندادن null."},"os":{"type":"string","nullable":true,"description":"سیستم‌عامل: Android، iOS، Windows، macOS، Linux یا ChromeOS؛ در صورت تشخیص‌ندادن null."},"kind":{"type":"string","description":"نوع دستگاه: mobile (موبایل/تبلت)، desktop (رایانه) یا unknown.","enum":["mobile","desktop","unknown"]}}},"ApiChatMessage":{"type":"object","description":"یک پیام چت ثبت‌شده در جلسه.","required":["senderName","externalUserId","message","chatId","isPrivate","sentAt"],"properties":{"senderName":{"type":"string","description":"نام فرستنده."},"externalUserId":{"type":"string","description":"شناسهٔ کاربر فرستنده."},"message":{"type":"string","description":"متن پیام."},"chatId":{"type":"string","description":"شناسهٔ گفتگو: MAIN-PUBLIC-GROUP-CHAT برای چت عمومی؛ مقادیر دیگر چت خصوصی‌اند."},"isPrivate":{"type":"boolean","description":"آیا پیام در چت خصوصی ارسال شده است."},"sentAt":{"type":"string","format":"date-time","description":"زمان ارسال (UTC)."}}},"ApiDuplicateParticipantGroup":{"type":"object","description":"هشدار: چند ردیف در participants که ظاهراً یک نفرند (نام یکسان) ولی external-user-id جدا دارند.","required":["name","externalUserIds","combinedPresenceSeconds"],"properties":{"name":{"type":"string","description":"نام مشترک ردیف‌های مشکوک."},"externalUserIds":{"type":"array","items":{"type":"string"},"description":"شناسه‌های جداگانه‌ای که در participants با همین نام دیده شده‌اند."},"combinedPresenceSeconds":{"type":"integer","format":"int64","description":"مجموع مدت واقعی حضور همهٔ این ردیف‌ها روی هم (اجتماع بازه‌ها) به ثانیه."}}},"ApiPageOfApiSessionDetail":{"type":"object","description":"پوشش همهٔ فهرست‌های صفحه‌بندی‌شده.","required":["items","page","pageSize","totalCount","totalPages"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ApiSessionDetail"},"description":"نتایج همین صفحه."},"page":{"type":"integer","format":"int32","description":"شمارهٔ صفحهٔ جاری (از ۱)."},"pageSize":{"type":"integer","format":"int32","description":"حداکثر تعداد ردیف در هر صفحه."},"totalCount":{"type":"integer","format":"int32","description":"تعداد کل نتایج منطبق با فیلترها، در تمام صفحات."},"totalPages":{"type":"integer","format":"int32","description":"تعداد کل صفحات."}}},"ApiClassAttendance":{"type":"object","description":"جمع‌بندی حضور افراد در همهٔ جلسه‌های یک کلاس در یک بازهٔ زمانی.","required":["externalMeetingId","from","to","thresholdPercent","sessionCount","sessions","participants"],"properties":{"externalMeetingId":{"type":"string","description":"شناسهٔ کلاس که جمع‌بندی برای آن است."},"from":{"type":"string","format":"date-time","nullable":true,"description":"ابتدای بازهٔ درخواستی (UTC)؛ اگر تعیین نشده باشد null."},"to":{"type":"string","format":"date-time","nullable":true,"description":"انتهای بازهٔ درخواستی (UTC) (این لحظه جزو بازه نیست)؛ اگر تعیین نشده باشد null."},"thresholdPercent":{"type":"integer","format":"int32","description":"آستانهٔ درصد حضور که اعمال شده است."},"sessionCount":{"type":"integer","format":"int32","description":"تعداد جلسه‌های کلاس در بازه."},"sessions":{"type":"array","items":{"$ref":"#/components/schemas/ApiClassSessionRef"},"description":"جلسه‌های بازه به ترتیب زمان."},"participants":{"type":"array","items":{"$ref":"#/components/schemas/ApiClassParticipant"},"description":"جمع‌بندی هر فرد؛ ابتدا مدرسان و سپس بقیه به ترتیب نام."}}},"ApiClassSessionRef":{"type":"object","description":"مشخصات یک جلسه در جمع‌بندی کلاس.","required":["sessionId","localDate","sequenceInDay","startedAt","endedAt","durationSeconds","baselineSeconds","participantCount"],"properties":{"sessionId":{"type":"string","format":"uuid","description":"شناسهٔ یکتای جلسه (UUID)."},"localDate":{"type":"string","description":"تاریخ میلادی روز برگزاری به وقت تهران.","format":"date"},"sequenceInDay":{"type":"integer","format":"int32","description":"شمارهٔ جلسه در همان روز (از ۱)."},"startedAt":{"type":"string","format":"date-time","description":"زمان شروع جلسه (UTC)."},"endedAt":{"type":"string","format":"date-time","nullable":true,"description":"زمان پایان جلسه (UTC)؛ برای جلسهٔ در حال برگزاری null."},"durationSeconds":{"type":"integer","format":"int64","description":"مدت جلسه به ثانیه."},"baselineSeconds":{"type":"integer","format":"int64","description":"مدت حضور پرحضورترین فرد جلسه (مبنای ۱۰۰٪) به ثانیه."},"participantCount":{"type":"integer","format":"int32","description":"تعداد شرکت‌کنندگان جلسه."}}},"ApiClassParticipant":{"type":"object","description":"جمع‌بندی حضور یک فرد در جلسه‌های کلاس.","required":["externalUserId","name","role","sessionsAttended","sessionsAbsent","sessionsBelowThreshold","totalPresenceSeconds","averagePercent","sessions"],"properties":{"externalUserId":{"type":"string","description":"شناسهٔ ثابت کاربر در سامانهٔ شما."},"name":{"type":"string","description":"آخرین نامی که کاربر در این جلسه‌ها داشته است."},"role":{"type":"string","description":"moderator اگر کاربر دست‌کم در یکی از جلسه‌ها مدرس بوده باشد؛ در غیر این صورت viewer.","enum":["moderator","viewer"]},"sessionsAttended":{"type":"integer","format":"int32","description":"تعداد جلسه‌های بازه که کاربر در آن‌ها حضور داشته است."},"sessionsAbsent":{"type":"integer","format":"int32","description":"تعداد جلسه‌های بازه که کاربر در آن‌ها اصلاً وارد نشده است."},"sessionsBelowThreshold":{"type":"integer","format":"int32","description":"تعداد جلسه‌های حاضر که درصد حضور در آن‌ها کمتر از آستانه بوده است."},"totalPresenceSeconds":{"type":"integer","format":"int64","description":"مجموع مدت واقعی حضور در همهٔ جلسه‌های بازه به ثانیه."},"averagePercent":{"type":"number","format":"double","description":"میانگین درصد حضور روی همهٔ جلسه‌های بازه؛ غیبت ۰٪ حساب می‌شود."},"sessions":{"type":"array","items":{"$ref":"#/components/schemas/ApiClassParticipantSession"},"description":"جزئیات حضور در جلسه‌هایی که کاربر در آن‌ها بوده است."}}},"ApiClassParticipantSession":{"type":"object","description":"حضور یک فرد در یکی از جلسه‌های کلاس.","required":["sessionId","presenceSeconds","attendancePercent","belowThreshold","joinCount"],"properties":{"sessionId":{"type":"string","format":"uuid","description":"شناسهٔ جلسه (یکی از موارد sessions در همین پاسخ)."},"presenceSeconds":{"type":"integer","format":"int64","description":"مدت واقعی حضور در این جلسه به ثانیه."},"attendancePercent":{"type":"number","format":"double","description":"درصد حضور در این جلسه نسبت به پرحضورترین فرد همان جلسه."},"belowThreshold":{"type":"boolean","description":"true اگر درصد حضور در این جلسه کمتر از آستانه باشد."},"joinCount":{"type":"integer","format":"int32","description":"تعداد دفعات ورود در این جلسه."}}},"ApiErrorEnvelope":{"type":"object","description":"قالب همهٔ پاسخ‌های خطا.","required":["error"],"properties":{"error":{"allOf":[{"$ref":"#/components/schemas/ApiError"}],"description":"جزئیات خطا."}}},"ApiError":{"type":"object","description":"جزئیات خطا.","required":["code","message"],"properties":{"code":{"type":"string","description":"کد ماشین‌خوان خطا (برای تصمیم‌گیری در برنامهٔ شما)؛ فهرست کدها در راهنمای API آمده است."},"message":{"type":"string","description":"پیام فارسی قابل‌نمایش دربارهٔ علت خطا."}}}},"parameters":{"ExternalMeetingIdQuery":{"name":"externalMeetingId","in":"query","description":"فقط جلسه‌های این کلاس (مطابقت دقیق با \u0060meetingID\u0060 سامانهٔ شما). مثال: \u0060class42\u0060","schema":{"type":"string"}},"ExternalMeetingIdPrefixQuery":{"name":"externalMeetingIdPrefix","in":"query","description":"جلسه‌هایی که شناسهٔ کلاسشان با این مقدار شروع می‌شود؛ برای زمانی که شناسهٔ هر جلسه یکتا است. مثال: \u0060class42-\u0060","schema":{"type":"string"}},"CourseCodeQuery":{"name":"courseCode","in":"query","description":"فقط جلسه‌های درسی که در پنل با این کد تعریف و تطبیق داده شده است. مثال: \u0060MATH101\u0060","schema":{"type":"string"}},"StatusQuery":{"name":"status","in":"query","description":"\u0060live\u0060: جلسه‌های در حال برگزاری — \u0060ended\u0060: جلسه‌های پایان‌یافته. بدون این پارامتر هر دو برگردانده می‌شوند.","schema":{"type":"string","enum":["live","ended"]}},"Date":{"name":"date","in":"query","description":"یک روز مشخص به وقت تهران؛ تاریخ میلادی (\u00602026-09-20\u0060) یا شمسی (\u00601405-06-29\u0060). همزمان با \u0060from\u0060/\u0060to\u0060 قابل ارسال نیست.","schema":{"type":"string"}},"From":{"name":"from","in":"query","description":"ابتدای بازه (بر اساس زمان شروع جلسه). تاریخ تنها (روز تهرانی) یا زمان کامل ISO-8601 **همراه offset**، مانند \u00602026-09-20T08:00:00\u002B03:30\u0060 یا \u00602026-09-20T04:30:00Z\u0060. علامت \u0060\u002B\u0060 در آدرس باید به‌صورت \u0060%2B\u0060 کدگذاری شود؛ زمان بدون offset پذیرفته نمی‌شود.","schema":{"type":"string"}},"To":{"name":"to","in":"query","description":"انتهای بازه. اگر تاریخ تنها باشد کل همان روز را شامل می‌شود؛ اگر زمان کامل باشد، همان لحظه جزو بازه نیست. فاصلهٔ \u0060from\u0060 تا \u0060to\u0060 حداکثر ۳۶۶ روز است.","schema":{"type":"string"}},"Threshold":{"name":"threshold","in":"query","description":"آستانهٔ درصد حضور (۰ تا ۱۰۰). \u0060belowThreshold\u0060 برای کسانی \u0060true\u0060 می‌شود که درصد حضورشان کمتر از این عدد باشد.","schema":{"type":"integer","minimum":0,"maximum":100,"default":70}},"Include":{"name":"include","in":"query","description":"فیلدهای اضافه، با ویرگول جدا شده (مثلاً \u0060ip,chat\u0060).\n\n- \u0060ip\u0060: آی‌پی و مرورگر هر بازهٔ حضور\n- \u0060chat\u0060: متن پیام‌های چت\n\nهر مورد نیازمند مجوز همان کلید است، وگرنه پاسخ ۴۰۳ با کد \u0060forbidden_include\u0060 برمی‌گردد.","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string","enum":["ip","chat"]}}},"Page":{"name":"page","in":"query","description":"شمارهٔ صفحه (از ۱).","schema":{"type":"integer","minimum":1,"default":1}}},"responses":{"BadRequest":{"description":"درخواست نامعتبر: پارامتر ناقص یا نادرست است. پیام فارسی علت را دقیق می‌گوید.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"},"examples":{"invalidParameter":{"summary":"پارامتر نامعتبر","value":{"error":{"code":"invalid_parameter","message":"threshold باید عددی بین 0 تا 100 باشد."}}},"missingParameter":{"summary":"پارامتر الزامی ارسال نشده","value":{"error":{"code":"missing_parameter","message":"یکی از date یا from الزامی است."}}},"invalidRange":{"summary":"بازهٔ زمانی معکوس","value":{"error":{"code":"invalid_range","message":"to باید بعد از from باشد."}}}}}}},"Unauthorized":{"description":"احراز هویت ناموفق: کلید ارسال نشده، ناشناخته، لغوشده یا منقضی است.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"},"examples":{"missingKey":{"summary":"کلید ارسال نشده","value":{"error":{"code":"missing_api_key","message":"کلید API ارسال نشده است. آن را در هدر X-Api-Key یا Authorization: Bearer بفرستید."}}},"invalidKey":{"summary":"کلید نامعتبر","value":{"error":{"code":"invalid_api_key","message":"کلید API نامعتبر است."}}},"revokedKey":{"summary":"کلید لغو شده","value":{"error":{"code":"api_key_revoked","message":"این کلید لغو شده است."}}},"expiredKey":{"summary":"کلید منقضی","value":{"error":{"code":"api_key_expired","message":"این کلید منقضی شده است."}}}}}}},"Forbidden":{"description":"دسترسی مجاز نیست: کلید مجوز \u0060include\u0060 درخواستی را ندارد، یا موسسه غیرفعال است.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"},"examples":{"forbiddenInclude":{"summary":"بدون مجوز آی‌پی/چت","value":{"error":{"code":"forbidden_include","message":"این کلید اجازهٔ دریافت آی‌پی (include=ip) را ندارد. از مدیر پنل بخواهید کلید جدیدی با این مجوز بسازد."}}},"instituteInactive":{"summary":"موسسه غیرفعال","value":{"error":{"code":"institute_inactive","message":"موسسهٔ مربوط به این کلید غیرفعال است."}}}}}}},"NotFound":{"description":"یافت نشد: جلسه یا کلاس برای موسسهٔ صاحب کلید وجود ندارد (داده‌های موسسه‌های دیگر هرگز آشکار نمی‌شوند). پیام، نام موسسهٔ کلید و شناسهٔ دریافت‌شده را نشان می‌دهد تا علت (شناسهٔ اشتباه یا کلیدِ موسسهٔ دیگر) را بتوانید پیدا کنید.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"},"examples":{"sessionNotFound":{"summary":"جلسه یافت نشد","value":{"error":{"code":"session_not_found","message":"جلسه‌ای با شناسهٔ «fb0faf96d84506c92ce517df930267c0d703287b-1789965606958» برای موسسهٔ «دانشگاه نمونه» (صاحب این کلید) یافت نشد. شناسه را بدون فاصله یا نویسهٔ اضافی وارد کنید (می‌توانید sessionId یا internalMeetingId خروجی /meetings را بردارید) و مطمئن شوید کلید برای همان موسسه‌ای ساخته شده که سرور کلاس به آن تعلق دارد."}}},"classNotFound":{"summary":"کلاس یافت نشد","value":{"error":{"code":"class_not_found","message":"هیچ جلسه‌ای با external-meeting-id «class42» برای موسسهٔ «دانشگاه نمونه» (صاحب این کلید) ثبت نشده است. شناسه را دقیق بررسی کنید و مطمئن شوید کلید برای همان موسسه‌ای ساخته شده که سرور کلاس به آن تعلق دارد."}}}}}}},"TooManyRequests":{"description":"تعداد درخواست‌ها از سقف مجاز (پیش‌فرض ۱۲۰ در دقیقه برای هر کلید) بیشتر شده است. پس از مدت هدر \u0060Retry-After\u0060 دوباره تلاش کنید.","headers":{"Retry-After":{"description":"تعداد ثانیه‌ای که باید تا درخواست بعدی صبر کرد.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorEnvelope"},"examples":{"rateLimited":{"summary":"سقف نرخ","value":{"error":{"code":"rate_limited","message":"تعداد درخواست‌ها بیش از حد مجاز (120 در دقیقه) است. 60 ثانیهٔ دیگر دوباره تلاش کنید."}}}}}}}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"کلید API موسسه (مانند \u0060itl_...\u0060) را که مدیر پنل ساخته است در همین قسمت وارد کنید. کلید فقط در حافظهٔ همین صفحه نگه‌داری می‌شود و با بستن یا تازه‌سازی صفحه پاک می‌شود."},"BearerAuth":{"type":"http","scheme":"bearer","description":"روش جایگزین: همان کلید در هدر \u0060Authorization: Bearer \u003Ckey\u003E\u0060. فقط یکی از دو روش را وارد کنید."}}}}