پلتفرم کلاس آنلاین رسا لاینIntegration APIv1

مرجع API یکپارچگی LMS

API REST برای LMS سفارشی ایرانی — مدل Adobe Connect: مدیریت از سرور + آدرس ورود (Launch URL) برای کاربر نهایی. هر عملیات شامل تابع SDK، پارامترها، نمونه JSON و کد Node.js / Python / PHP است.

API مدیریت

دانش‌آموز · کلاس · عضویت · مصرف

آدرس ورود

ورود یک‌کلیکی به کلاس (۱۰ دقیقه)

وب‌هوک

class.created · enrollment.created

جریان end-to-end

  1. upsertStudent() — ثبت کاربر LMS
  2. createClass() — ساخت کلاس (idempotent)
  3. enrollMembers() — عضویت
  4. issueLaunchUrl() — هدایت کاربر به کلاس
  5. 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)
POST/v1/integration/students

ثبت / به‌روزرسانی دانش‌آموز

کاربر LMS را با externalUserId پایدار در پلتفرم ثبت می‌کند. اگر قبلاً وجود داشته باشد به‌روز می‌شود (200). در غیر این صورت 201. قبل از اولین ثبت، phone یا email الزامی است.

scope: students:write

تابع SDK: upsertStudent(input)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
externalUserIdstringبلهشناسهٔ پایدار کاربر در LMS (مثلاً user_id جدول users)
phonestringخیرموبایل ایران — 09xxxxxxxxx (برای کاربر جدید یکی از phone/email لازم)
emailstringخیرایمیل (جایگزین phone برای کاربر جدید)
displayNamestringخیرنام نمایشی در کلاس

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
userIduuidبلهشناسهٔ داخلی پلتفرم
externalUserIdstringبلههمان externalUserId ارسالی
createdbooleanبلهtrue = کاربر جدید ساخته شد
requestIduuidبلهکد پیگیری پشتیبانی
apiVersionstringبلهنسخه قرارداد (۱)

خطاهای محتمل

codeHTTPزمان
EXTERNAL_USER_ID_REQUIRED400externalUserId خالی
IDENTIFIER_REQUIRED400کاربر جدید بدون phone/email
INVALID_PHONE400فرمت موبایل نامعتبر
PHONE_CONFLICT409موبایل متعلق به کاربر دیگر
MAX_STUDENTS409سقف دانش‌آموز پلن — 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

POST/v1/integration/classes

ساخت کلاس

کلاس جدید می‌سازد. با externalCourseId idempotent است: اگر قبلاً ساخته شده باشد همان classId برمی‌گردد (200, created: false). معلم با teacherExternalId یا teacherId مشخص می‌شود.

scope: classes:write

تابع SDK: createClass(input)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
externalCourseIdstringبلهشناسهٔ درس در LMS
namestringبلهنام کلاس (حداکثر ۲۵۵ کاراکتر)
teacherExternalIdstringخیرشناسهٔ معلم در LMS (ترجیحی)
teacherIduuidخیرUUID معلم در پلتفرم (جایگزین external)
startAtISO8601بلهشروع کلاس
endAtISO8601بلهپایان کلاس
maxParticipantsnumberخیرسقف شرکت‌کننده

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
classIduuidبلهشناسه کلاس پلتفرم
externalCourseIdstringبلهشناسه LMS
createdbooleanبلهfalse = کلاس از قبل وجود داشت
classobjectبله{ id, name, teacherId, startAt, endAt, status }

خطاهای محتمل

codeHTTPزمان
EXTERNAL_COURSE_ID_REQUIRED400externalCourseId یا name نبود
SCHEDULE_REQUIRED400startAt/endAt نبود
TEACHER_REQUIRED400معلم مشخص نشد
MAX_CLASSES409سقف کلاس — 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

GET/v1/integration/classes/{classId}

وضعیت کلاس

وضعیت کلاس را برمی‌گرداند. classId می‌تواند UUID پلتفرم یا externalCourseId باشد.

scope: classes:read

تابع SDK: getClass(classId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
classobjectبله{ id, name, schoolId, teacherId, startAt, endAt, status }

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس برای این مدرسه نیست

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}

GET/v1/integration/classes/{classId}/status

وضعیت تفصیلی کلاس

وضعیت زندهٔ کلاس (runtimeStatus)، lifecycle، آمار اعضا، حضور، ضبط‌ها و فایل‌ها را برمی‌گرداند.

scope: classes:read

تابع SDK: getClassStatus(classId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
classobjectبله{ id, externalCourseId?, name, status, lifecycle, runtimeStatus, timeZone, joinEarlyMinutes, … }
statsobjectبله{ membersByRole, membersTotal, uniqueAttendees, recordingsCount, sharedFilesCount, scheduledSessionsCount }

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس برای این مدرسه نیست

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}/status

GET/v1/integration/classes/{classId}/sessions

جلسات برنامه‌ریزی‌شده

لیست جلسات (occurrence) با occurrenceKey، زمان شروع/پایان، وضعیت، تعداد حاضرین و ضبط‌ها.

scope: classes:read

تابع SDK: getClassSessions(classId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
sessionsarrayبله{ occurrenceKey, index, startAt, endAt, status, label, attendees, recordingsCount }[]

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس یافت نشد

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}/sessions

GET/v1/integration/classes/{classId}/attendance

حضور و غیاب

بدون occurrenceKey: تجمیع حضور کل دوره. با ?occurrenceKey=… یا مسیر /sessions/{occurrenceKey}/attendance: حضور یک جلسه. externalUserId برای sync با LMS.

scope: attendance:read

تابع SDK: getClassAttendance(classId, occurrenceKey?)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
attendancearrayبله{ externalUserId?, userId, displayName, role, totalSeconds, totalMinutes, confirmedSeconds, sessionsCount }[]

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس یافت نشد
OCCURRENCE_NOT_FOUND404occurrenceKey در برنامه نیست
INVALID_OCCURRENCE400فرمت 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}/attendance

GET/v1/integration/classes/{classId}/recordings

ضبط‌های جلسه

لیست ضبط‌های کلاس. با ?occurrenceKey= فقط ضبط‌های همان جلسه. هر آیتم downloadUrl برای دریافت فایل با همان API key.

scope: recordings:read

تابع SDK: getClassRecordings(classId, occurrenceKey?)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
recordingsarrayبله{ id, fileName, sectionLabel, mimeType, sizeBytes, recordedAt, storageStatus, downloadUrl }[]

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس یافت نشد
OCCURRENCE_NOT_FOUND404occurrenceKey نامعتبر

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}/recordings

GET/v1/integration/classes/{classId}/files

فایل‌های اشتراکی جلسه

فایل‌هایی که در کلاس/جلسه به اشتراک گذاشته شده‌اند (PDF، اسلاید، …) با لینک دانلود.

scope: files:read

تابع SDK: getClassFiles(classId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
filesarrayبله{ id, fileName, originalName, mimeType, sizeBytes, uploadedAt, fromName, downloadUrl }[]

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس یافت نشد

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}/files

POST/v1/integration/events

ساخت جلسه / وبینار

جلسه (session) یا وبینار با externalEventId پایدار. settings.autoRecord برای ضبط خودکار. hostExternalId الزامی.

scope: events:write

تابع SDK: createEvent(input)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
externalEventIdstringبلهشناسهٔ پایدار در LMS
titlestringبلهعنوان
eventTypestringخیرsession | webinar
hostExternalIdstringبلهمیزبان — باید قبلاً provision شده باشد
settings.autoRecordbooleanخیرضبط خودکار

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
eventobjectبلهرویداد ساخته‌شده

خطاهای محتمل

codeHTTPزمان
MAX_EVENTS409سقف رویداد پلن

نمونه 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

GET/v1/integration/events/{eventId}/status

وضعیت جلسه / وبینار

وضعیت، تنظیمات autoRecord، آمار شرکت‌کنندگان، حضور و ضبط.

scope: events:read

تابع SDK: getEventStatus(eventId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
eventobjectبلهشامل settings.autoRecord و publicJoinUrl
statsobjectبلهparticipantsTotal, uniqueAttendees, recordingsCount

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست

نمونه Response

{ "event": { "eventType": "webinar", "settings": { "autoRecord": true } }, "stats": { "participantsTotal": 120 } }

پیاده‌سازی

GET /api/v1/integration/events/{eventId}/status
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/events/{eventId}/status

PUT/v1/integration/classes/{classId}/members

عضویت در کلاس

اعضا را به‌صورت گروهی ثبت می‌کند. هر member باید قبلاً با upsertStudent ثبت شده باشد (یا teacher با external id).

scope: members:write

تابع SDK: enrollMembers(classId, members)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
membersarrayبلهآرایهٔ { externalUserId, role? } — role: student|teacher

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
classIduuidبلهشناسه کلاس
addedarrayبلهلیست { externalUserId, userId, role }

خطاهای محتمل

codeHTTPزمان
MEMBERS_REQUIRED400members خالی
USER_NOT_FOUND404externalUserId provision نشده
SCHOOL_ACCESS_DENIED403دانش‌آموز به مدرسه تعلق ندارد

نمونه 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

POST/v1/integration/classes/{classId}/launch-url

ورود به کلاس (Launch URL)

لینک امضاشده ۱۰ دقیقه‌ای برای ورود کاربر به کلاس صادر می‌کند. کاربر را با HTTP 302 به فیلد url هدایت کنید. API key هرگز در مرورگر نباشد.

scope: launch:issue

تابع SDK: issueLaunchUrl(classId, input)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
externalUserIdstringبلهکاربر LMS
rolestringخیرstudent (پیش‌فرض) | teacher | instructor
displayNamestringخیرنام در جلسه
phonestringخیربرای auto-provision در صورت نیاز
emailstringخیرجایگزین phone
redirectPathstringخیرمسیر پس از لاگین (پیش‌فرض: /meetings/simple?roomId=…)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
urlstringبلهLaunch URL — redirect کاربر
launchUrlstringبلههمان url
expiresAtISO8601بلهانقضای توکن (~۱۰ دقیقه)
classIduuidبلهشناسه کلاس
userIduuidبلهشناسه کاربر پلتفرم

خطاهای محتمل

codeHTTPزمان
MEETING_MINUTES_EXHAUSTED429سقف دقیقه جلسه ماه جاری
EXTERNAL_USER_ID_REQUIRED400externalUserId نبود

نمونه 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

GET/v1/integration/plan

پلن فعال و پورتال

جزئیات پلن فعال (limits، overage)، وضعیت اشتراک، سهمیه‌ها و در صورت فعال بودن، URL پورتال/دامنهٔ اختصاصی مدرسه.

scope: usage:read

تابع SDK: getPlan()

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
subscriptionobjectبلهstatus, planNameFa, expiresAt, isActive, productScope
planobjectخیرlimits (maxStudents, maxClasses, maxLiveEvents, maxConcurrentCameras, maxConcurrentParticipants, …)
portalobjectبله{ live, url, domainMode, displayName }
quotasobjectبلههمان ساختار GET /usage

خطاهای محتمل

codeHTTPزمان
SUBSCRIPTION_EXPIRED402اشتراک منقضی

نمونه 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

PATCH/v1/integration/classes/{classId}

ویرایش کلاس

نام، زمان، معلم، lifecycle و settings.autoRecord (ضبط خودکار) را به‌روز می‌کند.

scope: classes:write

تابع SDK: updateClass(classId, patch)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
namestringخیرنام کلاس
startAt / endAtdatetimeخیربازهٔ زمانی
settings.autoRecordbooleanخیرtrue = ضبط خودکار (پیش‌فرض)
lifecyclestringخیرdraft | published | archived

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
classobjectبلهکلاس به‌روز شده

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس نیست

نمونه 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}

DELETE/v1/integration/classes/{classId}

آرشیو / حذف کلاس

کلاس را archived می‌کند (soft delete). دادهٔ حضور و ضبط حفظ می‌شود.

scope: classes:write

تابع SDK: archiveClass(classId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
archivedbooleanبلهtrue

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس نیست

نمونه Response

{ "classId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "archived": true }

پیاده‌سازی

DELETE /api/v1/integration/classes/{classId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx

URL کامل: DELETE /api/v1/integration/classes/{classId}

GET/v1/integration/classes/{classId}/sessions/{occurrenceKey}/attendance

حضور یک جلسه (occurrence)

حضور و غیاب یک جلسهٔ مشخص با occurrenceKey (از GET /sessions).

scope: attendance:read

تابع SDK: getSessionAttendance(classId, occurrenceKey)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
occurrenceKeystringبلهکلید جلسه
attendancearrayبلهلیست حاضرین همان جلسه

خطاهای محتمل

codeHTTPزمان
CLASS_NOT_FOUND404کلاس یافت نشد
OCCURRENCE_NOT_FOUND404occurrenceKey در برنامه نیست

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/classes/{classId}/sessions/{occurrenceKey}/attendance

GET/v1/integration/events/{eventId}

دریافت رویداد

جزئیات رویداد به‌همراه وضعیت و آمار (مشابه /status).

scope: events:read

تابع SDK: getEvent(eventId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
eventobjectبلهرویداد با settings و publicJoinUrl
statsobjectبلهآمار شرکت‌کنندگان

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست

نمونه Response

{ "event": { "eventType": "webinar", "title": "وبینار معرفی" }, "stats": { "participantsTotal": 120 } }

پیاده‌سازی

GET /api/v1/integration/events/{eventId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/events/{eventId}

PATCH/v1/integration/events/{eventId}

ویرایش رویداد

عنوان، زمان، تنظیمات autoRecord و lifecycle رویداد را به‌روز می‌کند.

scope: events:write

تابع SDK: updateEvent(eventId, patch)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
titlestringخیرعنوان
settings.autoRecordbooleanخیرضبط خودکار

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
eventobjectبلهرویداد به‌روز شده

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست

نمونه 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}

DELETE/v1/integration/events/{eventId}

آرشیو رویداد

رویداد را archived می‌کند (soft delete).

scope: events:write

تابع SDK: archiveEvent(eventId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
archivedbooleanبلهtrue

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست

نمونه Response

{ "eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "archived": true }

پیاده‌سازی

DELETE /api/v1/integration/events/{eventId}
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx

URL کامل: DELETE /api/v1/integration/events/{eventId}

GET/v1/integration/events/{eventId}/attendance

حضور رویداد

لیست حضور شرکت‌کنندگان وبینار/جلسه.

scope: attendance:read

تابع SDK: getEventAttendance(eventId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
attendancearrayبلهحاضرین با externalUserId و مدت حضور

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست

نمونه Response

{ "attendance": [{ "externalUserId": "lms-1", "totalMinutes": 45 }] }

پیاده‌سازی

GET /api/v1/integration/events/{eventId}/attendance
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/events/{eventId}/attendance

GET/v1/integration/events/{eventId}/recordings

ضبط‌های رویداد

لیست ضبط‌های رویداد با downloadUrl.

scope: recordings:read

تابع SDK: getEventRecordings(eventId)

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
recordingsarrayبلهضبط‌ها با downloadUrl

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست

نمونه 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_xxxxxxxxxxxxxxxx

URL کامل: GET /api/v1/integration/events/{eventId}/recordings

POST/v1/integration/events/{eventId}/launch-url

ورود به رویداد (Launch URL)

لینک امضاشده ۱۰ دقیقه‌ای برای ورود به وبینار/جلسه.

scope: launch:issue

تابع SDK: issueEventLaunchUrl(eventId, input)

پارامترهای درخواست (Body)

فیلدنوعالزامیتوضیح
externalUserIdstringبلهکاربر LMS
rolestringخیرparticipant (پیش‌فرض) | host | teacher

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
urlstringبلهLaunch URL
expiresAtISO8601بلهانقضای توکن

خطاهای محتمل

codeHTTPزمان
EVENT_NOT_FOUND404رویداد نیست
EXTERNAL_USER_ID_REQUIRED400externalUserId نبود

نمونه 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

GET/v1/integration/usage

مصرف و سهمیه

نمای لحظه‌ای اشتراک، سهمیه (دانش‌آموز، کلاس، ذخیره، دقیقه جلسه، نفر همزمان) و فعالیت API برای رسم نمودار در داشبورد LMS.

scope: usage:read

تابع SDK: getUsage()

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
subscriptionobjectبلهstatus, planNameFa, expiresAt, daysRemaining
quotasobjectبلهstudents, classes, storageBytes, meetingMinutes, concurrentParticipants — هر کدام used/limit/remaining/percentUsed
apiActivityobjectبلهlast24h, last30dDaily[]

خطاهای محتمل

codeHTTPزمان
SUBSCRIPTION_EXPIRED402اشتراک منقضی — 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

GET/v1/integration/errors

فهرست کدهای خطا

کاتالوگ عمومی خطاها — بدون API key. برای sync در SDK یا نمایش در پنل LMS.

تابع SDK: listErrors()

فیلدهای پاسخ

فیلدنوعالزامیتوضیح
errorsarrayبله{ 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"
}

در حال بارگذاری کاتالوگ خطا…

دانلود JSON کاتالوگ

محدودیت نرخ و Idempotency

  • ۱۲۰ درخواست در دقیقه به ازای هر کلید API
  • خطای RATE_LIMIT_EXCEEDED (429) + هدر Retry-After
  • Idempotency-Key روی POST /students، /classes و /events — TTL ۲۴ ساعت