API mitra untuk data sekolah.
Baca sekolah, staf, tagihan, dan kehadiran dari SekolahOS lewat REST yang berversi dan bertanda tangan.
Domain yang terbuka untuk mitra
Setiap kunci API diberi cakupan per domain dan per sekolah. Cakupan yang tidak diminta tidak pernah terbaca.
Roster & staf
Yayasan, sekolah, dan staf aktif beserta NIP dan status kepegawaian. NIK hanya untuk kunci dengan roster.pii.
Keuangan
Tagihan beserta total, terbayar, dan sisa terutang. Uang selalu string 2dp dengan mata uang.
Kehadiran
Rekap harian per siswa dengan enam status tetap. Foto gerbang tidak pernah diekspor.
Kejadian
Webhook bertanda tangan atau polling untuk siswa baru, pembayaran ter-settle, dan rotasi kunci. Berlaku untuk semua kunci.
Autentikasi yang bisa dirotasi
Pasangan key-id dan secret per yayasan, ditandatangani HMAC-SHA256 per permintaan. Rotasi tanpa downtime.
Aman diulang
Setiap endpoint mutasi menerima Idempotency-Key. Kirim ulang permintaan yang sama dan Anda mendapat respons yang sama.
Webhook, dengan jaring polling
Webhook bertanda tangan untuk kejadian domain, lima kali coba ulang — dan endpoint events bila penerima Anda pernah mati.
Sambungkan asisten AI ke data sekolah.
Claude Desktop, Claude Code, Cursor, dan klien MCP lain bisa membaca data sekolah Anda lewat Partner API. Hanya-baca, dibatasi cakupan kunci, dan setiap panggilan tercatat.
Terbitkan kunci
Admin yayasan menerbitkan kunci di konsol, menu Mitra & kunci API. Mulai dari kunci ak_test_ dengan cakupan baca saja.
Pasang jembatan
Unduh satu berkas Python tanpa dependensi. Klien MCP tidak bisa membuat tanda tangan HMAC, jadi jembatan ini menandatangani tiap permintaan di komputer Anda. Secret tidak pernah dikirim.
Tambahkan ke klien
Tempel konfigurasi untuk klien Anda, mulai ulang, lalu tanyakan: sekolah apa saja yang bisa dibaca? Panduan lengkap ada di bagian MCP dokumentasi.
ak_test_ selalu menabrak data sintetis dan tidak pernah mengirim notifikasi ke orang tua. Gunakan itu untuk seluruh pengembangan.
Autentikasi
Setiap permintaan membawa key-id di header dan tanda tangan HMAC-SHA256 atas timestamp + method + path + body. Secret tidak pernah dikirim. Permintaan dengan timestamp lebih dari 300 detik ditolak.
X-EduCore-Key-Id: ak_live_7Qp2R9 X-EduCore-Signature: t=1789632650,v1=9f... Idempotency-Key: 4f0a1b2c-...
| Aturan | Ketentuan |
|---|---|
| Cakupan | Ditetapkan yayasan, per domain dan per sekolah. Tidak bisa diperluas oleh mitra. |
| Rotasi | Dua kunci aktif maksimal 30 hari. Kunci lama jadi read-only saat hari ke-23. |
| IP allow-list | Opsional per kunci. Permintaan dari alamat di luar daftar ditolak dengan IP_NOT_ALLOWED. |
Versi & deprekasi
Versi ada di path, bukan header. Perubahan yang memutus kontrak selalu menjadi /api/v2/ baru; penambahan field opsional bukan perubahan yang memutus. Versi lama hidup 12 bulan setelah pengganti keluar, dan mengirim header Sunset sejak hari pertama.
Konvensi
| Aturan | Bentuk |
|---|---|
| Paginasi | ?cursor=&limit= → {results, next_cursor} |
| Uang | {"amount": "1500000.00", "currency": "IDR"} |
| Waktu | ISO 8601 UTC — 2026-09-17T02:14:00Z |
| Idempotensi | Idempotency-Key — disimpan 7 hari |
Format uang adalah string dengan mata uang di level objek yang sama. Jangan pernah menjumlahkan lintas mata uang: endpoint agregat mengembalikan array total per mata uang, bukan satu angka.
Error
Semua error adalah RFC 9457 application/problem+json. Cabangkan logika pada code — string SCREAMING_SNAKE yang stabil — bukan pada prosa detail.
{
"type": "/errors/scope-denied",
"title": "Scope not granted",
"status": 403,
"detail": "Key lacks roster.pii",
"instance": "/api/v1/partner/_",
"code": "SCOPE_DENIED"
}
| Status | Code |
|---|---|
| 401 | SIGNATURE_INVALID |
| 403 | SCOPE_DENIED |
| 400 | VALIDATION_ERROR |
| 403 | IP_NOT_ALLOWED |
| 404 | NOT_FOUND |
| 409 | IDEMPOTENCY_MISMATCH |
| 429 | RATE_LIMITED |
Webhook & polling
Webhook adalah jalur utama. Daftarkan satu endpoint HTTPS per lingkungan, terima POST bertanda tangan, dan balas 2xx dalam 5 detik. Kegagalan dicoba ulang lima kali hingga 6 jam; setelahnya kejadian tetap tersedia lewat polling.
| Kejadian | Dipicu saat |
|---|---|
roster.student.enrolled | Siswa aktif baru di sekolah dalam cakupan kunci |
finance.payment.settled | Pembayaran VA atau QRIS ter-settle dan teralokasi |
integration.key.rotated | Kunci dirotasi — 30 hari sebelum yang lama mati |
Endpoint
| Metode & jalur | Keterangan |
|---|---|
GET /partner/foundationsroster.read | Yayasan yang memberi Anda akses |
GET /partner/schoolsroster.read | Sekolah dalam cakupan kunci |
GET /partner/staff?school_idroster.read | Staf aktif beserta NIP dan status, kursor terpaginasi; NIK hanya dengan roster.pii |
GET /partner/invoices?statusfinance.read | Tagihan dan sisa terutang |
GET /partner/attendance/daily?dateattendance.read | Kehadiran harian per siswa |
GET /partner/events?since=cursor— | Aliran kejadian — alternatif webhook |
POST /partner/mcpsesuai alat | Server MCP hanya-baca untuk asisten AI; lihat bagian MCP |
POST /partner/webhooks— | Daftarkan endpoint penerima webhook |
MCP
Endpoint POST /partner/mcp adalah server Model Context Protocol (HTTP, mode JSON tanpa sesi) di belakang autentikasi dan batas laju yang sama dengan endpoint lain. Isinya alat hanya-baca; kunci hanya melihat alat sesuai cakupannya, dan baris yang dikembalikan sama persis dengan REST: batas sekolah, batas yayasan, NIK hanya dengan roster.pii.
Persiapan
- Python 3.8 ke atas di komputer yang menjalankan klien AI (
python3 --version; di Windows gunakanpython). - Unduh educore_mcp_bridge.py dan simpan di folder tetap, misalnya
~/sekolahos/. - Periksa berkas sebelum dijalankan:
shasum -a 256 educore_mcp_bridge.pyharus menghasilkan177f51df1cb11dea603f32a03809fca400a4f0e5e5dfbabf378f50d3f3a0c080. - Minta admin yayasan menerbitkan kunci. Catat key-id dan secret; secret hanya tampil sekali.
- Simpan secret di pengelola kata sandi atau keychain sistem operasi, bukan di berkas yang disinkronkan atau masuk ke git. Minta kunci dengan scope seperlunya dan batasi dengan daftar IP.
| Variabel | Isi |
|---|---|
EDUCORE_API_BASE | https://schoolday.live/api/v1/ |
EDUCORE_KEY_ID | Key-id, misalnya ak_test_7Qp2R9 |
EDUCORE_KEY_SECRET | Secret yang tampil saat kunci diterbitkan |
Panduan per klien
Claude Desktop
- Buka Settings → Developer → Edit Config; berkas
claude_desktop_config.jsonterbuka. - Tambahkan server berikut, ganti path dan kredensialnya:
{
"mcpServers": {
"sekolahos": {
"command": "python3",
"args": ["/Users/anda/sekolahos/educore_mcp_bridge.py"],
"env": {
"EDUCORE_API_BASE": "https://schoolday.live/api/v1/",
"EDUCORE_KEY_ID": "ak_test_...",
"EDUCORE_KEY_SECRET": "..."
}
}
}
}
- Keluar sepenuhnya dari Claude Desktop lalu buka lagi. Ikon alat muncul di kotak percakapan dengan server sekolahos.
Claude Code
Satu perintah di terminal:
claude mcp add sekolahos \ --env EDUCORE_API_BASE=https://schoolday.live/api/v1/ \ --env EDUCORE_KEY_ID=ak_test_... \ --env EDUCORE_KEY_SECRET=... \ -- python3 ~/sekolahos/educore_mcp_bridge.py
Periksa dengan claude mcp list, atau ketik /mcp di dalam sesi.
Cursor
Buat ~/.cursor/mcp.json (atau .cursor/mcp.json di proyek) dengan isi yang sama seperti Claude Desktop di atas, lalu aktifkan server di Settings → MCP.
Klien MCP lain
Klien apa pun yang mendukung server MCP lokal (stdio) cukup diberi perintah python3 /path/educore_mcp_bridge.py dan tiga variabel lingkungan di atas. Untuk menguji tanpa klien AI, gunakan MCP Inspector:
EDUCORE_API_BASE=https://schoolday.live/api/v1/ \ EDUCORE_KEY_ID=ak_test_... EDUCORE_KEY_SECRET=... \ npx @modelcontextprotocol/inspector python3 educore_mcp_bridge.py
Uji cepat dari terminal
Kirim satu pesan JSON-RPC ke jembatan; balasannya daftar alat yang boleh dipakai kunci Anda:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python3 educore_mcp_bridge.py
Alat yang tersedia
| Alat | Cakupan | Fungsi |
|---|---|---|
list_schools | roster.read | Sekolah dalam cakupan kunci |
list_classes | roster.read | Rombel dengan tingkat dan tahun ajaran. Filter: school_id |
list_students | roster.read roster.pii | Siswa dengan NIS, status, dan kelas saat ini; NISN hanya dengan roster.pii; NIK, tanggal lahir, alamat, dan kontak wali tidak pernah dikirim. Filter: school_id, class_group_id, status, q (sebagian nama) |
list_staff | roster.read roster.pii | Staf aktif dengan NIP dan status; NIK hanya dengan roster.pii. Filter: school_id |
list_invoices | finance.read | Tagihan dan sisa terutang. Filter: status, school_id, student_id, period, due_from, due_to |
get_daily_attendance | attendance.read | Kehadiran siswa untuk satu hari. Wajib: date (YYYY-MM-DD) |
get_attendance_range | attendance.read | Kehadiran siswa untuk rentang tanggal (maksimal 31 hari). Wajib: date_from, date_to. Filter: school_id, student_id, status |
Semua alat menerima limit (maksimal 200) dan before_id. Jawaban memuat next_before_id; teruskan nilainya untuk halaman berikutnya. Tidak ada alat tulis; pendaftaran webhook tetap hanya lewat REST. Satu kunci boleh memanggil alat paling banyak 120 kali per menit (di bawah batas 600 permintaan).
Jika tidak jalan
| Gejala | Penyebab dan perbaikan |
|---|---|
| Server tidak muncul di klien | Path berkas salah atau Python tidak ada di PATH. Jalankan perintah command dari terminal untuk melihat pesan galatnya. |
SIGNATURE_INVALID | Secret salah, kunci sudah dicabut atau kedaluwarsa, atau jam komputer melenceng lebih dari 300 detik. |
IP_NOT_ALLOWED | Kunci memakai IP allow-list dan alamat komputer Anda belum terdaftar. |
SCOPE_DENIED | Kunci tidak punya cakupan untuk alat itu. Minta admin yayasan menerbitkan kunci baru dengan cakupan yang sesuai. |
RATE_LIMITED | Batas 600 permintaan per menit per kunci dipakai bersama REST dan MCP. |
Untuk reseller dan integrator
Setiap kunci API berlaku untuk tepat satu yayasan; tidak ada kunci yang menjangkau beberapa yayasan. Jika Anda mengintegrasikan banyak sekolah, minta admin tiap yayasan menerbitkan kunci untuk Anda di konsol, menu Mitra & kunci API, dengan cakupan seminimal mungkin. Simpan tiap rahasia di brankas Anda sendiri, satu per yayasan.
Untuk membantu konfigurasi di dalam konsol, admin yayasan membuat login staf biasa dengan peran seperlunya. Reseller tidak mendapat akses lintas yayasan.
Batas laju
| Batas | Nilai |
|---|---|
| Permintaan per kunci | 600 / menit |
| Baris maksimal per halaman kursor | 200 |
| Interval polling tercepat yang diizinkan | 30 detik |
Melewati batas menghasilkan 429 RATE_LIMITED dengan header Retry-After.