welearninsync API

ดึงข้อมูลวิชา คาบ รายชื่อ และผลคะแนนของสถาบันไปใช้ในระบบทะเบียน Moodle หรือ dashboard — อ่านอย่างเดียว

สเปก OpenAPI 3.1 (JSON)

การยืนยันตัวตน

ผู้ดูแลสถาบันสร้าง API key ที่ "จัดการสถาบัน → เชื่อมต่อระบบอื่น" แล้วส่งใน header ทุก request key หนึ่งอันเห็นเฉพาะข้อมูลของสถาบันนั้น และเฉพาะสิทธิ์ที่เลือกไว้

curl https://<your-domain>/api/v1/courses \
  -H "Authorization: Bearer lsk_live_…"

เฉลยของคำถามไม่ออกทาง API ไม่ว่า key จะมีสิทธิ์อะไร

Endpoints

Pathสิทธิ์ได้อะไร
GET /api/v1/coursessessions:readวิชาทั้งหมดของสถาบัน
GET /api/v1/courses/{courseId}/sessionssessions:readคาบเรียนของวิชา
GET /api/v1/courses/{courseId}/rosterroster:readรายชื่อนักศึกษาของวิชา
GET /api/v1/courses/{courseId}/gradebookresults:readการเข้าเรียน การมีส่วนร่วม คะแนน ทั้งวิชา
GET /api/v1/sessions/{sessionId}/resultsresults:readผลของคาบเดียว รายคนและรายข้อ

ข้อตกลง

{
  "error": {
    "type": "insufficient_scope",
    "message": "missing scope roster:read",
    "request_id": "0b6f…"
  }
}

Webhooks

ให้ welearninsync แจ้งระบบของคุณทันทีที่เกิดเหตุการณ์ เช่น จบคาบแล้วดึงผลคะแนนต่อได้เลย — payload มีแค่ชนิด event กับ id ไม่มีข้อมูลนักศึกษา ให้ปลายทางเรียก API ด้วย key ของตัวเองเพื่อดึงข้อมูล

Event

POST /your-endpoint
X-LearnSync-Event: session.ended
X-LearnSync-Delivery: 7d1c…
X-LearnSync-Signature: t=1790000000,v1=5f2a…

{"data": {"course_id": "…", "session_id": "…"}, "event": "session.ended", "created_at": "2026-10-01T09:30:00+00:00"}

ตรวจลายเซ็น

ทุก request มี header X-LearnSync-Signature: t=<เวลา>,v1=<HMAC-SHA256> คำนวณจาก "<t>.<body ดิบ>" ด้วย signing secret ของ webhook นั้น — ตรวจกับ body ดิบก่อน parse JSON และปฏิเสธถ้า t เก่ากว่า 5 นาที (กัน replay)

import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the exact bytes received — verify BEFORE JSON.parse
export function verify(secret, signatureHeader, rawBody) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(signatureHeader ?? "");
  if (!m) return false;
  if (Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false; // replay
  const expected = createHmac("sha256", secret).update(`${m[1]}.${rawBody}`).digest();
  return timingSafeEqual(expected, Buffer.from(m[2], "hex"));
}

ตอบ 2xx ภายใน 10 วินาที = สำเร็จ อย่างอื่นส่งใหม่หลัง 1 นาที, 5 นาที, 30 นาที, 2 ชม., 6 ชม. แล้วเลิก — ปลายทางที่ไม่ตอบติดกันถูกพักอัตโนมัติ กดส่งซ้ำได้จากหน้าจัดการ

event อาจมาไม่ตรงลำดับ และอาจมาซ้ำ — ใช้ X-LearnSync-Delivery กันประมวลผลซ้ำ และ created_at ใน payload เพื่อเรียงลำดับ