مرجع API یکپارچگی LMS
API REST برای LMS سفارشی ایرانی — مدل Adobe Connect: مدیریت از سرور + آدرس ورود (Launch URL) برای کاربر نهایی. هر عملیات شامل تابع SDK، پارامترها، نمونه JSON و کد Node.js / Python / PHP است.
API مدیریت
دانشآموز · کلاس · عضویت · مصرف
آدرس ورود
ورود یککلیکی به کلاس (۱۰ دقیقه)
وبهوک
class.created · enrollment.created
جریان end-to-end
upsertStudent()— ثبت کاربر LMScreateClass()— ساخت کلاس (idempotent)enrollMembers()— عضویتissueLaunchUrl()— هدایت کاربر به کلاسgetUsage()— نمودار سهمیه در LMS
احراز هویت و هدرها
Base URL: /api — کلید از پنل مدرسه → API LMS
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx Content-Type: application/json Idempotency-Key: unique-key # POST /students، /classes و /events X-Request-Id: (در پاسخ) # برای پشتیبانی X-RateLimit-Remaining: (در پاسخ) # محدودیت نرخ
Scopes
| Scope | کاربرد |
|---|---|
| students:write | ثبت و بهروزرسانی دانشآموز (POST /students) |
| classes:write | ساخت و ویرایش کلاس (POST /classes) |
| classes:read | خواندن وضعیت کلاس، جلسات و آمار (GET /classes/…) |
| members:write | عضویت گروهی (PUT /classes/…/members) |
| launch:issue | صدور Launch URL ورود به کلاس |
| usage:read | خواندن مصرف و سهمیه (GET /usage) |
| attendance:read | حضور و غیاب کلاس یا یک جلسه |
| recordings:read | لیست ضبطها و فرادادهی ضبط |
| files:read | دانلود فایل ضبط و پیوستها |
| events:write | ساخت، ویرایش و آرشیو جلسه/وبینار |
| events:read | وضعیت رویداد، حضور و ضبطها |
کلاینت SDK کامل
کلاس آماده با مدیریت خطا (ArtaIntegrationError)، Idempotency-Key، و تمام متدها. فایل را در پروژه LMS کپی کنید.
/**
* ArtaIntegrationClient — Node.js 18+
* npm: فقط fetch داخلی (یا node-fetch)
*/
export class ArtaIntegrationError extends Error {
constructor(
message: string,
public code: string,
public requestId: string | null,
public details?: Record<string, unknown>,
public httpStatus?: number,
) {
super(message);
this.name = 'ArtaIntegrationError';
}
}
export class ArtaIntegrationClient {
constructor(
private apiKey: string,
private baseUrl = '/api',
) {}
private async request<T>(
method: string,
path: string,
opts?: { body?: unknown; idempotencyKey?: string; auth?: boolean },
): Promise<T & { requestId: string; apiVersion: string }> {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (opts?.auth !== false) headers.Authorization = `Bearer ${this.apiKey}`;
if (opts?.idempotencyKey) headers['Idempotency-Key'] = opts.idempotencyKey;
const res = await fetch(`${this.baseUrl}${path}`, {
method,
headers,
body: opts?.body != null ? JSON.stringify(opts.body) : undefined,
});
const data = await res.json();
if (!res.ok) {
throw new ArtaIntegrationError(
data.error?.message ?? res.statusText,
data.error?.code ?? 'HTTP_ERROR',
data.requestId ?? res.headers.get('X-Request-Id'),
data.error?.details,
res.status,
);
}
return data;
}
async upsertStudent(input: {
externalUserId: string;
phone?: string;
email?: string;
displayName?: string;
}, opts?: { idempotencyKey?: string }) {
return this.request('POST', '/v1/integration/students', {
body: input,
idempotencyKey: opts?.idempotencyKey ?? `student:${input.externalUserId}`,
});
}
async createClass(input: {
externalCourseId: string;
name: string;
teacherExternalId?: string;
startAt: string;
endAt: string;
maxParticipants?: number;
}) {
return this.request('POST', '/v1/integration/classes', {
body: input,
idempotencyKey: `class:${input.externalCourseId}`,
});
}
async getClass(classId: string) {
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}`);
}
async getClassStatus(classId: string) {
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}/status`);
}
async getClassSessions(classId: string) {
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}/sessions`);
}
async getClassAttendance(classId: string, occurrenceKey?: string) {
const q = occurrenceKey ? `?occurrenceKey=${encodeURIComponent(occurrenceKey)}` : '';
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}/attendance${q}`);
}
async getClassRecordings(classId: string, occurrenceKey?: string) {
const q = occurrenceKey ? `?occurrenceKey=${encodeURIComponent(occurrenceKey)}` : '';
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}/recordings${q}`);
}
async downloadRecording(classId: string, recordingId: string): Promise<ArrayBuffer> {
const res = await fetch(`${this.baseUrl}/v1/integration/classes/${encodeURIComponent(classId)}/recordings/${recordingId}/download`, {
headers: { Authorization: `Bearer ${this.apiKey}` },
});
if (!res.ok) throw new Error(await res.text());
return res.arrayBuffer();
}
async getClassFiles(classId: string) {
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}/files`);
}
async createEvent(input: Record<string, unknown>, opts?: { idempotencyKey?: string }) {
return this.request('POST', '/v1/integration/events', { body: input, idempotencyKey: opts?.idempotencyKey });
}
async getEventStatus(eventId: string) {
return this.request('GET', `/v1/integration/events/${encodeURIComponent(eventId)}/status`);
}
async enrollMembers(classId: string, members: { externalUserId: string; role?: 'student'|'teacher' }[]) {
return this.request('PUT', `/v1/integration/classes/${encodeURIComponent(classId)}/members`, {
body: { members },
});
}
async issueLaunchUrl(classId: string, input: {
externalUserId: string;
role?: 'student' | 'teacher' | 'instructor';
displayName?: string;
}) {
return this.request('POST', `/v1/integration/classes/${encodeURIComponent(classId)}/launch-url`, {
body: input,
});
}
async getPlan() {
return this.request('GET', '/v1/integration/plan');
}
async updateClass(classId: string, patch: Record<string, unknown>) {
return this.request('PATCH', `/v1/integration/classes/${encodeURIComponent(classId)}`, { body: patch });
}
async archiveClass(classId: string) {
return this.request('DELETE', `/v1/integration/classes/${encodeURIComponent(classId)}`);
}
async getSessionAttendance(classId: string, occurrenceKey: string) {
return this.request('GET', `/v1/integration/classes/${encodeURIComponent(classId)}/sessions/${encodeURIComponent(occurrenceKey)}/attendance`);
}
async getEvent(eventId: string) {
return this.request('GET', `/v1/integration/events/${encodeURIComponent(eventId)}`);
}
async updateEvent(eventId: string, patch: Record<string, unknown>) {
return this.request('PATCH', `/v1/integration/events/${encodeURIComponent(eventId)}`, { body: patch });
}
async archiveEvent(eventId: string) {
return this.request('DELETE', `/v1/integration/events/${encodeURIComponent(eventId)}`);
}
async getEventAttendance(eventId: string) {
return this.request('GET', `/v1/integration/events/${encodeURIComponent(eventId)}/attendance`);
}
async getEventRecordings(eventId: string) {
return this.request('GET', `/v1/integration/events/${encodeURIComponent(eventId)}/recordings`);
}
async issueEventLaunchUrl(eventId: string, input: { externalUserId: string; role?: string }) {
return this.request('POST', `/v1/integration/events/${encodeURIComponent(eventId)}/launch-url`, { body: input });
}
async getUsage() {
return this.request('GET', '/v1/integration/usage');
}
async listErrors() {
return this.request('GET', '/v1/integration/errors', { auth: false });
}
}
// ─── مثال end-to-end ───
const client = new ArtaIntegrationClient(process.env.ARTA_API_KEY!);
await client.upsertStudent({ externalUserId: 'u1', phone: '09121234567', displayName: 'علی' });
const cls = await client.createClass({
externalCourseId: 'c1', name: 'ریاضی', teacherExternalId: 't1',
startAt: new Date().toISOString(),
endAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(),
});
await client.enrollMembers(cls.classId, [{ externalUserId: 'u1', role: 'student' }]);
const launch = await client.issueLaunchUrl(cls.classId, { externalUserId: 'u1' });
// res.redirect(302, launch.url)/v1/integration/studentsثبت / بهروزرسانی دانشآموز
کاربر LMS را با externalUserId پایدار در پلتفرم ثبت میکند. اگر قبلاً وجود داشته باشد بهروز میشود (200). در غیر این صورت 201. قبل از اولین ثبت، phone یا email الزامی است.
تابع SDK: upsertStudent(input)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| externalUserId | string | بله | شناسهٔ پایدار کاربر در LMS (مثلاً user_id جدول users) |
| phone | string | خیر | موبایل ایران — 09xxxxxxxxx (برای کاربر جدید یکی از phone/email لازم) |
| string | خیر | ایمیل (جایگزین phone برای کاربر جدید) | |
| displayName | string | خیر | نام نمایشی در کلاس |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| userId | uuid | بله | شناسهٔ داخلی پلتفرم |
| externalUserId | string | بله | همان externalUserId ارسالی |
| created | boolean | بله | true = کاربر جدید ساخته شد |
| requestId | uuid | بله | کد پیگیری پشتیبانی |
| apiVersion | string | بله | نسخه قرارداد (۱) |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EXTERNAL_USER_ID_REQUIRED | 400 | externalUserId خالی |
| IDENTIFIER_REQUIRED | 400 | کاربر جدید بدون phone/email |
| INVALID_PHONE | 400 | فرمت موبایل نامعتبر |
| PHONE_CONFLICT | 409 | موبایل متعلق به کاربر دیگر |
| MAX_STUDENTS | 409 | سقف دانشآموز پلن — details شامل limit/used/remaining |
نمونه Request
{
"externalUserId": "lms-user-4821",
"phone": "09121234567",
"displayName": "علی رضایی"
}نمونه Response
{
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"externalUserId": "lms-user-4821",
"created": true,
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"apiVersion": "1"
}پیادهسازی
POST /api/v1/integration/students
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: unique-key-per-operation
{
"externalUserId": "lms-user-4821",
"phone": "09121234567",
"displayName": "علی رضایی"
}URL کامل: POST /api/v1/integration/students
/v1/integration/classesساخت کلاس
کلاس جدید میسازد. با externalCourseId idempotent است: اگر قبلاً ساخته شده باشد همان classId برمیگردد (200, created: false). معلم با teacherExternalId یا teacherId مشخص میشود.
تابع SDK: createClass(input)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| externalCourseId | string | بله | شناسهٔ درس در LMS |
| name | string | بله | نام کلاس (حداکثر ۲۵۵ کاراکتر) |
| teacherExternalId | string | خیر | شناسهٔ معلم در LMS (ترجیحی) |
| teacherId | uuid | خیر | UUID معلم در پلتفرم (جایگزین external) |
| startAt | ISO8601 | بله | شروع کلاس |
| endAt | ISO8601 | بله | پایان کلاس |
| maxParticipants | number | خیر | سقف شرکتکننده |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| classId | uuid | بله | شناسه کلاس پلتفرم |
| externalCourseId | string | بله | شناسه LMS |
| created | boolean | بله | false = کلاس از قبل وجود داشت |
| class | object | بله | { id, name, teacherId, startAt, endAt, status } |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EXTERNAL_COURSE_ID_REQUIRED | 400 | externalCourseId یا name نبود |
| SCHEDULE_REQUIRED | 400 | startAt/endAt نبود |
| TEACHER_REQUIRED | 400 | معلم مشخص نشد |
| MAX_CLASSES | 409 | سقف کلاس — details: limit, used, remaining |
نمونه Request
{
"externalCourseId": "course-math-10",
"name": "ریاضی پایه دهم",
"teacherExternalId": "teacher-12",
"startAt": "2026-06-01T08:00:00+03:30",
"endAt": "2026-09-01T08:00:00+03:30",
"maxParticipants": 80
}نمونه Response
{
"classId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"externalCourseId": "course-math-10",
"created": true,
"class": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "ریاضی پایه دهم",
"status": "published",
"startAt": "2026-06-01T04:30:00.000Z",
"endAt": "2026-09-01T04:30:00.000Z"
},
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"apiVersion": "1"
}پیادهسازی
POST /api/v1/integration/classes
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: unique-key-per-operation
{
"externalCourseId": "course-math-10",
"name": "ریاضی پایه دهم",
"teacherExternalId": "teacher-12",
"startAt": "2026-06-01T08:00:00+03:30",
"endAt": "2026-09-01T08:00:00+03:30",
"maxParticipants": 80
}URL کامل: POST /api/v1/integration/classes
/v1/integration/classes/{classId}وضعیت کلاس
وضعیت کلاس را برمیگرداند. classId میتواند UUID پلتفرم یا externalCourseId باشد.
تابع SDK: getClass(classId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| class | object | بله | { id, name, schoolId, teacherId, startAt, endAt, status } |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس برای این مدرسه نیست |
نمونه Response
{
"class": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "ریاضی پایه دهم",
"status": "published",
"teacherId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"startAt": "2026-06-01T04:30:00.000Z",
"endAt": "2026-09-01T04:30:00.000Z"
},
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"apiVersion": "1"
}پیادهسازی
GET /api/v1/integration/classes/{classId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}
/v1/integration/classes/{classId}/statusوضعیت تفصیلی کلاس
وضعیت زندهٔ کلاس (runtimeStatus)، lifecycle، آمار اعضا، حضور، ضبطها و فایلها را برمیگرداند.
تابع SDK: getClassStatus(classId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| class | object | بله | { id, externalCourseId?, name, status, lifecycle, runtimeStatus, timeZone, joinEarlyMinutes, … } |
| stats | object | بله | { membersByRole, membersTotal, uniqueAttendees, recordingsCount, sharedFilesCount, scheduledSessionsCount } |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس برای این مدرسه نیست |
نمونه Response
{
"class": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"externalCourseId": "lms-course-42",
"name": "ریاضی پایه دهم",
"status": "published",
"lifecycle": "published",
"runtimeStatus": "scheduled",
"timeZone": "Asia/Tehran",
"joinEarlyMinutes": 15
},
"stats": {
"membersByRole": { "student": 28, "teacher": 1 },
"membersTotal": 29,
"uniqueAttendees": 24,
"recordingsCount": 6,
"sharedFilesCount": 3,
"scheduledSessionsCount": 12
}
}پیادهسازی
GET /api/v1/integration/classes/{classId}/status
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}/status
/v1/integration/classes/{classId}/sessionsجلسات برنامهریزیشده
لیست جلسات (occurrence) با occurrenceKey، زمان شروع/پایان، وضعیت، تعداد حاضرین و ضبطها.
تابع SDK: getClassSessions(classId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| sessions | array | بله | { occurrenceKey, index, startAt, endAt, status, label, attendees, recordingsCount }[] |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس یافت نشد |
نمونه Response
{
"classId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"totalSessions": 12,
"sessions": [{
"occurrenceKey": "2026-06-01T08:00:00.000Z",
"index": 0,
"startAt": "2026-06-01T08:00:00.000Z",
"endAt": "2026-06-01T09:30:00.000Z",
"status": "ended",
"label": "جلسه ۱",
"attendees": 22,
"recordingsCount": 1
}]
}پیادهسازی
GET /api/v1/integration/classes/{classId}/sessions
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}/sessions
/v1/integration/classes/{classId}/attendanceحضور و غیاب
بدون occurrenceKey: تجمیع حضور کل دوره. با ?occurrenceKey=… یا مسیر /sessions/{occurrenceKey}/attendance: حضور یک جلسه. externalUserId برای sync با LMS.
تابع SDK: getClassAttendance(classId, occurrenceKey?)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| attendance | array | بله | { externalUserId?, userId, displayName, role, totalSeconds, totalMinutes, confirmedSeconds, sessionsCount }[] |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس یافت نشد |
| OCCURRENCE_NOT_FOUND | 404 | occurrenceKey در برنامه نیست |
| INVALID_OCCURRENCE | 400 | فرمت occurrenceKey نامعتبر |
نمونه Response
{
"occurrenceKey": "2026-06-01T08:00:00.000Z",
"attendance": [{
"externalUserId": "lms-user-4821",
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"displayName": "علی رضایی",
"role": "student",
"totalSeconds": 4920,
"totalMinutes": 82,
"confirmedSeconds": 4800,
"sessionsCount": 1
}]
}پیادهسازی
GET /api/v1/integration/classes/{classId}/attendance
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}/attendance
/v1/integration/classes/{classId}/recordingsضبطهای جلسه
لیست ضبطهای کلاس. با ?occurrenceKey= فقط ضبطهای همان جلسه. هر آیتم downloadUrl برای دریافت فایل با همان API key.
تابع SDK: getClassRecordings(classId, occurrenceKey?)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| recordings | array | بله | { id, fileName, sectionLabel, mimeType, sizeBytes, recordedAt, storageStatus, downloadUrl }[] |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس یافت نشد |
| OCCURRENCE_NOT_FOUND | 404 | occurrenceKey نامعتبر |
نمونه Response
{
"recordings": [{
"id": "rec-uuid",
"fileName": "session-1.webm",
"recordedAt": "2026-06-01T08:05:00.000Z",
"sizeBytes": 52428800,
"downloadUrl": "https://api.example.com/api/v1/integration/classes/{classId}/recordings/rec-uuid/download"
}]
}پیادهسازی
GET /api/v1/integration/classes/{classId}/recordings
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}/recordings
/v1/integration/classes/{classId}/filesفایلهای اشتراکی جلسه
فایلهایی که در کلاس/جلسه به اشتراک گذاشته شدهاند (PDF، اسلاید، …) با لینک دانلود.
تابع SDK: getClassFiles(classId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| files | array | بله | { id, fileName, originalName, mimeType, sizeBytes, uploadedAt, fromName, downloadUrl }[] |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس یافت نشد |
نمونه Response
{
"files": [{
"id": "file-uuid",
"fileName": "slides.pdf",
"originalName": "فصل-۳.pdf",
"mimeType": "application/pdf",
"sizeBytes": 1048576,
"downloadUrl": "https://api.example.com/api/v1/integration/classes/{classId}/files/file-uuid/download"
}]
}پیادهسازی
GET /api/v1/integration/classes/{classId}/files
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}/files
/v1/integration/eventsساخت جلسه / وبینار
جلسه (session) یا وبینار با externalEventId پایدار. settings.autoRecord برای ضبط خودکار. hostExternalId الزامی.
تابع SDK: createEvent(input)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| externalEventId | string | بله | شناسهٔ پایدار در LMS |
| title | string | بله | عنوان |
| eventType | string | خیر | session | webinar |
| hostExternalId | string | بله | میزبان — باید قبلاً provision شده باشد |
| settings.autoRecord | boolean | خیر | ضبط خودکار |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| event | object | بله | رویداد ساختهشده |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| MAX_EVENTS | 409 | سقف رویداد پلن |
نمونه Request
{
"externalEventId": "webinar-99",
"title": "وبینار معرفی",
"eventType": "webinar",
"hostExternalId": "teacher-1",
"startsAt": "2026-06-01T10:00:00Z",
"endsAt": "2026-06-01T11:30:00Z",
"settings": { "autoRecord": true }
}نمونه Response
{ "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "externalEventId": "webinar-99", "created": true }پیادهسازی
POST /api/v1/integration/events
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: unique-key-per-operation
{
"externalEventId": "webinar-99",
"title": "وبینار معرفی",
"eventType": "webinar",
"hostExternalId": "teacher-1",
"startsAt": "2026-06-01T10:00:00Z",
"endsAt": "2026-06-01T11:30:00Z",
"settings": { "autoRecord": true }
}URL کامل: POST /api/v1/integration/events
/v1/integration/events/{eventId}/statusوضعیت جلسه / وبینار
وضعیت، تنظیمات autoRecord، آمار شرکتکنندگان، حضور و ضبط.
تابع SDK: getEventStatus(eventId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| event | object | بله | شامل settings.autoRecord و publicJoinUrl |
| stats | object | بله | participantsTotal, uniqueAttendees, recordingsCount |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
نمونه Response
{ "event": { "eventType": "webinar", "settings": { "autoRecord": true } }, "stats": { "participantsTotal": 120 } }پیادهسازی
GET /api/v1/integration/events/{eventId}/status
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/events/{eventId}/status
/v1/integration/classes/{classId}/membersعضویت در کلاس
اعضا را بهصورت گروهی ثبت میکند. هر member باید قبلاً با upsertStudent ثبت شده باشد (یا teacher با external id).
تابع SDK: enrollMembers(classId, members)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| members | array | بله | آرایهٔ { externalUserId, role? } — role: student|teacher |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| classId | uuid | بله | شناسه کلاس |
| added | array | بله | لیست { externalUserId, userId, role } |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| MEMBERS_REQUIRED | 400 | members خالی |
| USER_NOT_FOUND | 404 | externalUserId provision نشده |
| SCHOOL_ACCESS_DENIED | 403 | دانشآموز به مدرسه تعلق ندارد |
نمونه Request
{
"members": [
{ "externalUserId": "lms-user-4821", "role": "student" },
{ "externalUserId": "teacher-12", "role": "teacher" }
]
}نمونه Response
{
"classId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"added": [
{ "externalUserId": "lms-user-4821", "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "role": "student" }
],
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"apiVersion": "1"
}پیادهسازی
PUT /api/v1/integration/classes/{classId}/members
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{
"members": [
{ "externalUserId": "lms-user-4821", "role": "student" },
{ "externalUserId": "teacher-12", "role": "teacher" }
]
}URL کامل: PUT /api/v1/integration/classes/{classId}/members
/v1/integration/classes/{classId}/launch-urlورود به کلاس (Launch URL)
لینک امضاشده ۱۰ دقیقهای برای ورود کاربر به کلاس صادر میکند. کاربر را با HTTP 302 به فیلد url هدایت کنید. API key هرگز در مرورگر نباشد.
تابع SDK: issueLaunchUrl(classId, input)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| externalUserId | string | بله | کاربر LMS |
| role | string | خیر | student (پیشفرض) | teacher | instructor |
| displayName | string | خیر | نام در جلسه |
| phone | string | خیر | برای auto-provision در صورت نیاز |
| string | خیر | جایگزین phone | |
| redirectPath | string | خیر | مسیر پس از لاگین (پیشفرض: /meetings/simple?roomId=…) |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| url | string | بله | Launch URL — redirect کاربر |
| launchUrl | string | بله | همان url |
| expiresAt | ISO8601 | بله | انقضای توکن (~۱۰ دقیقه) |
| classId | uuid | بله | شناسه کلاس |
| userId | uuid | بله | شناسه کاربر پلتفرم |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| MEETING_MINUTES_EXHAUSTED | 429 | سقف دقیقه جلسه ماه جاری |
| EXTERNAL_USER_ID_REQUIRED | 400 | externalUserId نبود |
نمونه Request
{
"externalUserId": "lms-user-4821",
"role": "student",
"displayName": "علی رضایی"
}نمونه Response
{
"url": "https://app.example.ir/login?integration=1&integration_token=eyJhbGciOiJIUzI1NiJ9&redirect=%2Fmeetings%2Fsimple%3FroomId%3Dabc",
"expiresAt": "2026-05-29T12:10:00.000Z",
"classId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"apiVersion": "1"
}پیادهسازی
POST /api/v1/integration/classes/{classId}/launch-url
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{
"externalUserId": "lms-user-4821",
"role": "student",
"displayName": "علی رضایی"
}URL کامل: POST /api/v1/integration/classes/{classId}/launch-url
/v1/integration/planپلن فعال و پورتال
جزئیات پلن فعال (limits، overage)، وضعیت اشتراک، سهمیهها و در صورت فعال بودن، URL پورتال/دامنهٔ اختصاصی مدرسه.
تابع SDK: getPlan()
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| subscription | object | بله | status, planNameFa, expiresAt, isActive, productScope |
| plan | object | خیر | limits (maxStudents, maxClasses, maxLiveEvents, maxConcurrentCameras, maxConcurrentParticipants, …) |
| portal | object | بله | { live, url, domainMode, displayName } |
| quotas | object | بله | همان ساختار GET /usage |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| SUBSCRIPTION_EXPIRED | 402 | اشتراک منقضی |
نمونه Response
{
"subscription": { "status": "active", "planNameFa": "حرفهای", "isActive": true },
"plan": { "limits": { "maxStudents": 800, "maxLiveEvents": 50 } },
"portal": { "live": true, "url": "https://portal.school.ir", "domainMode": "custom" }
}پیادهسازی
GET /api/v1/integration/plan Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
URL کامل: GET /api/v1/integration/plan
/v1/integration/classes/{classId}ویرایش کلاس
نام، زمان، معلم، lifecycle و settings.autoRecord (ضبط خودکار) را بهروز میکند.
تابع SDK: updateClass(classId, patch)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| name | string | خیر | نام کلاس |
| startAt / endAt | datetime | خیر | بازهٔ زمانی |
| settings.autoRecord | boolean | خیر | true = ضبط خودکار (پیشفرض) |
| lifecycle | string | خیر | draft | published | archived |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| class | object | بله | کلاس بهروز شده |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس نیست |
نمونه Request
{ "settings": { "autoRecord": true }, "name": "ریاضی — فصل ۲" }نمونه Response
{ "class": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "settings": { "autoRecord": true } } }پیادهسازی
PATCH /api/v1/integration/classes/{classId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{ "settings": { "autoRecord": true }, "name": "ریاضی — فصل ۲" }URL کامل: PATCH /api/v1/integration/classes/{classId}
/v1/integration/classes/{classId}آرشیو / حذف کلاس
کلاس را archived میکند (soft delete). دادهٔ حضور و ضبط حفظ میشود.
تابع SDK: archiveClass(classId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| archived | boolean | بله | true |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس نیست |
نمونه Response
{ "classId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "archived": true }پیادهسازی
DELETE /api/v1/integration/classes/{classId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: DELETE /api/v1/integration/classes/{classId}
/v1/integration/classes/{classId}/sessions/{occurrenceKey}/attendanceحضور یک جلسه (occurrence)
حضور و غیاب یک جلسهٔ مشخص با occurrenceKey (از GET /sessions).
تابع SDK: getSessionAttendance(classId, occurrenceKey)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| occurrenceKey | string | بله | کلید جلسه |
| attendance | array | بله | لیست حاضرین همان جلسه |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| CLASS_NOT_FOUND | 404 | کلاس یافت نشد |
| OCCURRENCE_NOT_FOUND | 404 | occurrenceKey در برنامه نیست |
نمونه Response
{
"occurrenceKey": "2026-06-01T08:00:00.000Z",
"attendance": [{
"externalUserId": "lms-user-4821",
"totalMinutes": 82
}]
}پیادهسازی
GET /api/v1/integration/classes/{classId}/sessions/{occurrenceKey}/attendance
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/classes/{classId}/sessions/{occurrenceKey}/attendance
/v1/integration/events/{eventId}دریافت رویداد
جزئیات رویداد بههمراه وضعیت و آمار (مشابه /status).
تابع SDK: getEvent(eventId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| event | object | بله | رویداد با settings و publicJoinUrl |
| stats | object | بله | آمار شرکتکنندگان |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
نمونه Response
{ "event": { "eventType": "webinar", "title": "وبینار معرفی" }, "stats": { "participantsTotal": 120 } }پیادهسازی
GET /api/v1/integration/events/{eventId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/events/{eventId}
/v1/integration/events/{eventId}ویرایش رویداد
عنوان، زمان، تنظیمات autoRecord و lifecycle رویداد را بهروز میکند.
تابع SDK: updateEvent(eventId, patch)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| title | string | خیر | عنوان |
| settings.autoRecord | boolean | خیر | ضبط خودکار |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| event | object | بله | رویداد بهروز شده |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
نمونه Request
{ "title": "وبینار — نسخهٔ نهایی", "settings": { "autoRecord": true } }نمونه Response
{ "event": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "وبینار — نسخهٔ نهایی" } }پیادهسازی
PATCH /api/v1/integration/events/{eventId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{ "title": "وبینار — نسخهٔ نهایی", "settings": { "autoRecord": true } }URL کامل: PATCH /api/v1/integration/events/{eventId}
/v1/integration/events/{eventId}آرشیو رویداد
رویداد را archived میکند (soft delete).
تابع SDK: archiveEvent(eventId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| archived | boolean | بله | true |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
نمونه Response
{ "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "archived": true }پیادهسازی
DELETE /api/v1/integration/events/{eventId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: DELETE /api/v1/integration/events/{eventId}
/v1/integration/events/{eventId}/attendanceحضور رویداد
لیست حضور شرکتکنندگان وبینار/جلسه.
تابع SDK: getEventAttendance(eventId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| attendance | array | بله | حاضرین با externalUserId و مدت حضور |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
نمونه Response
{ "attendance": [{ "externalUserId": "lms-1", "totalMinutes": 45 }] }پیادهسازی
GET /api/v1/integration/events/{eventId}/attendance
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/events/{eventId}/attendance
/v1/integration/events/{eventId}/recordingsضبطهای رویداد
لیست ضبطهای رویداد با downloadUrl.
تابع SDK: getEventRecordings(eventId)
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| recordings | array | بله | ضبطها با downloadUrl |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
نمونه Response
{ "recordings": [{ "id": "rec-1", "downloadUrl": "https://api.example.com/api/v1/integration/events/{eventId}/recordings/rec-1/download" }] }پیادهسازی
GET /api/v1/integration/events/{eventId}/recordings
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxURL کامل: GET /api/v1/integration/events/{eventId}/recordings
/v1/integration/events/{eventId}/launch-urlورود به رویداد (Launch URL)
لینک امضاشده ۱۰ دقیقهای برای ورود به وبینار/جلسه.
تابع SDK: issueEventLaunchUrl(eventId, input)
پارامترهای درخواست (Body)
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| externalUserId | string | بله | کاربر LMS |
| role | string | خیر | participant (پیشفرض) | host | teacher |
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| url | string | بله | Launch URL |
| expiresAt | ISO8601 | بله | انقضای توکن |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| EVENT_NOT_FOUND | 404 | رویداد نیست |
| EXTERNAL_USER_ID_REQUIRED | 400 | externalUserId نبود |
نمونه Request
{ "externalUserId": "lms-user-4821", "role": "participant" }نمونه Response
{
"url": "https://app.example.ir/login?integration=1&integration_token=eyJhbGciOiJIUzI1NiJ9",
"expiresAt": "2026-05-29T12:10:00.000Z",
"eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}پیادهسازی
POST /api/v1/integration/events/{eventId}/launch-url
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
{ "externalUserId": "lms-user-4821", "role": "participant" }URL کامل: POST /api/v1/integration/events/{eventId}/launch-url
/v1/integration/usageمصرف و سهمیه
نمای لحظهای اشتراک، سهمیه (دانشآموز، کلاس، ذخیره، دقیقه جلسه، نفر همزمان) و فعالیت API برای رسم نمودار در داشبورد LMS.
تابع SDK: getUsage()
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| subscription | object | بله | status, planNameFa, expiresAt, daysRemaining |
| quotas | object | بله | students, classes, storageBytes, meetingMinutes, concurrentParticipants — هر کدام used/limit/remaining/percentUsed |
| apiActivity | object | بله | last24h, last30dDaily[] |
خطاهای محتمل
| code | HTTP | زمان |
|---|---|---|
| SUBSCRIPTION_EXPIRED | 402 | اشتراک منقضی — details.renewalUrl |
نمونه Response
{
"subscription": { "status": "active", "planNameFa": "حرفهای", "daysRemaining": 186 },
"quotas": {
"students": { "used": 420, "limit": 500, "remaining": 80, "percentUsed": 84 },
"classes": { "used": 28, "limit": 30, "remaining": 2, "percentUsed": 93 },
"concurrentParticipants": { "used": 142, "limit": 300, "remaining": 158, "percentUsed": 47 }
},
"apiActivity": { "last24h": { "total": 142, "errors": 3 } }
}پیادهسازی
GET /api/v1/integration/usage Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
URL کامل: GET /api/v1/integration/usage
/v1/integration/errorsفهرست کدهای خطا
کاتالوگ عمومی خطاها — بدون API key. برای sync در SDK یا نمایش در پنل LMS.
تابع SDK: listErrors()
فیلدهای پاسخ
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
| errors | array | بله | { code, httpStatus, messageFa, hintFa? }[] |
نمونه Response
{ "errors": [{ "code": "MAX_CLASSES", "httpStatus": 409, "messageFa": "سقف تعداد کلاس مدرسه پر شده است." }] }پیادهسازی
GET /api/v1/integration/errors
URL کامل: GET /api/v1/integration/errors
وبهوک
URL را در پنل مدرسه ثبت کنید. امضا: هدر X-Webhook-Signature (HMAC-SHA256).
| eventType | زمان |
|---|---|
| class.created | پس از POST /classes (created: true) |
| enrollment.created | پس از POST /students یا PUT /members |
| class.ended | پس از پایان جلسه زنده |
| event.created | پس از POST /events (created: true) |
| event.updated | پس از PATCH /events |
| event.archived | پس از DELETE /events |
تابع verifyWebhook (Node.js)
import { createHmac } from 'crypto';
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(body).digest('hex');
return signature === expected;
}فرمت خطا و کاتالوگ
همهٔ پاسخهای ۴xx/۵xx ساختار یکسان دارند. فیلد requestId را در تیکت پشتیبانی بفرستید. فیلد docUrl به بخش مرتبط در همین مستندات اشاره میکند.
{
"error": {
"type": "integration_api_error",
"code": "MAX_CLASSES",
"message": "School class limit reached.",
"messageFa": "سقف تعداد کلاس مدرسه پر شده است.",
"hintFa": "پلن فعلی را ارتقا دهید یا کلاسهای قدیمی را آرشیو کنید — کد پیگیری: f47ac10b-58cc-4372-a567-0e02b2c3d479",
"details": { "limit": 30, "used": 30, "remaining": 0, "planName": "حرفهای" },
"docUrl": "/developers/integration-api#classes"
},
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"apiVersion": "1"
}در حال بارگذاری کاتالوگ خطا…
محدودیت نرخ و Idempotency
- ۱۲۰ درخواست در دقیقه به ازای هر کلید API
- خطای
RATE_LIMIT_EXCEEDED(429) + هدر Retry-After - Idempotency-Key روی
POST /students،/classesو/events— TTL ۲۴ ساعت