Partner & vendor integration

A partner API for school data.

Read schools, staff, invoices and attendance from SekolahOS through versioned, signed REST.

Integration surface

Domains open to partners

Every API key is scoped per domain and per school. A scope you did not request is never readable.

Data boundary SekolahOS does not store payroll or compensation data. The partner surface only exposes name, NIP and employment status — salary, wage components, deductions and payslips stay in your payroll system as the source of truth.
roster.read · roster.pii

Roster & staff

Foundation, schools and active staff with NIP and employment status. NIK only for keys with roster.pii.

finance.read

Finance

Invoices with total, paid and balance due. Money is always a 2dp string with its currency.

attendance.read

Attendance

Daily record per student with six fixed statuses. Gate photos are never exported.

events

Event

Signed webhooks or polling for new students, settled payments and key rotation. Applies to every key.

01

Authentication you can rotate

A key-id and secret pair per foundation, HMAC-SHA256 signed per request. Rotate with no downtime.

02

Safe to retry

Every mutating endpoint accepts an Idempotency-Key. Replay the same request and you get the same response, not a double effect.

03

Webhooks, with a polling net

Signed webhooks for domain events with five retries — and an events endpoint for when your receiver was down.

MCP

Connect an AI assistant to your school data.

Claude Desktop, Claude Code, Cursor and other MCP clients can read your school data through the Partner API. Read-only, limited to the key's scopes, and every call is logged.

01

Issue key

The foundation admin issues a key in the console, under Partners & API keys. Start with an ak_test_ key with read scopes only.

02

Install the bridge

Download one Python file with no dependencies. MCP clients cannot make an HMAC signature, so this bridge signs every request on your computer. The secret is never sent.

03

Add it to your client

Paste the configuration for your client, restart it, then ask: which schools can you read? The full guide is in the MCP section of the documentation.

Sandbox Keys prefixed ak_test_ always hit synthetic data and never send notifications to parents. Use it for all development.

Authentication

Every request carries a key-id header and an HMAC-SHA256 signature over timestamp + method + path + body. The secret is never sent. Requests with a timestamp older than 300 seconds are rejected.

X-EduCore-Key-Id:    ak_live_7Qp2R9
X-EduCore-Signature: t=1789632650,v1=9f...
Idempotency-Key:     4f0a1b2c-...
RuleTerms
ScopeSet by the foundation, per domain and per school. Cannot be widened by the partner.
RotationAt most two active keys for 30 days. The old key becomes read-only on day 23.
IP allow-listOptional per key. Requests from an address outside the list are refused with IP_NOT_ALLOWED.

Versioning & deprecation

The version lives in the path, not a header. Breaking changes always become a new /api/v2/; adding an optional field is not a breaking change. An old version lives for 12 months after its replacement ships, and sends a Sunset header from day one.

Conventions

RuleShape
Pagination?cursor=&limit= → {results, next_cursor}
Money{"amount": "1500000.00", "currency": "IDR"}
TimeISO 8601 UTC — 2026-09-17T02:14:00Z
IdempotencyIdempotency-Key — kept for 7 days

Money is a string with the currency at the same object level. Never sum across currencies: aggregate endpoints return an array of totals per currency, not a single number.

Error

All errors are RFC 9457 application/problem+json. Branch your logic on code — a stable SCREAMING_SNAKE string — not on the detail prose.

{
  "type":     "/errors/scope-denied",
  "title":    "Scope not granted",
  "status":   403,
  "detail":   "Key lacks roster.pii",
  "instance": "/api/v1/partner/_",
  "code":     "SCOPE_DENIED"
}
StatusCode
401SIGNATURE_INVALID
403SCOPE_DENIED
400VALIDATION_ERROR
403IP_NOT_ALLOWED
404NOT_FOUND
409IDEMPOTENCY_MISMATCH
429RATE_LIMITED

Webhooks & polling

Webhooks are the primary path. Register one HTTPS endpoint per environment, accept a signed POST, and answer 2xx within 5 seconds. Failures retry five times up to 6 hours; after that the event remains available by polling.

EventTriggered when
roster.student.enrolledA new active student at a school in the key's scope
finance.payment.settledA virtual-account or QRIS payment settled and was allocated
integration.key.rotatedA key is rotated — 30 days before the old one dies

Endpoint

Method & pathDescription
GET /partner/foundations
roster.read
Foundations that granted you access
GET /partner/schools
roster.read
Schools within the key's scope
GET /partner/staff?school_id
roster.read
Active staff with NIP and status, cursor-paginated; NIK only with roster.pii
GET /partner/invoices?status
finance.read
Invoices and outstanding balance
GET /partner/attendance/daily?date
attendance.read
Daily attendance per student
GET /partner/events?since=cursor
—
Event stream — a webhook alternative
POST /partner/mcp
per tool
Read-only MCP server for AI assistants; see the MCP section
POST /partner/webhooks
—
Register a webhook receiver

MCP

The POST /partner/mcp endpoint is a Model Context Protocol server (HTTP, sessionless JSON mode) behind the same authentication and rate limit as the other endpoints. It holds read-only tools; a key only sees the tools its scopes allow, and the rows returned are exactly those of REST: school limit, foundation limit, NIK only with roster.pii.

Do not paste the secret into an AI client The bridge reads the secret from an environment variable on your computer and never sends it; only the HMAC signature leaves. Do not keep the secret in a file that goes into a repository. Give production keys an IP allow-list and the smallest scopes possible.

Before you start

  1. Python 3.8 or newer on the computer that runs the AI client (python3 --version; on Windows use python).
  2. Download educore_mcp_bridge.py and keep it in a permanent folder, for example ~/sekolahos/.
  3. Check the file before running it: shasum -a 256 educore_mcp_bridge.py must print 177f51df1cb11dea603f32a03809fca400a4f0e5e5dfbabf378f50d3f3a0c080.
  4. Ask the foundation admin to issue a key. Note the key-id and secret; the secret is shown only once.
  5. Keep the secret in a password manager or the operating system keychain, not in a synced file or in git. Ask for a key with only the scopes you need and restrict it with an IP list.
VariablesText
EDUCORE_API_BASEhttps://schoolday.live/api/v1/
EDUCORE_KEY_IDThe key-id, for example ak_test_7Qp2R9
EDUCORE_KEY_SECRETThe secret shown when the key was issued

Guide by client

Claude Desktop
  1. Open Settings → Developer → Edit Config; the claude_desktop_config.json file opens.
  2. Add the following server, replacing the path and credentials:
{
  "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": "..."
      }
    }
  }
}
  1. Quit Claude Desktop completely and open it again. A tools icon appears in the chat box with the sekolahos server.
Claude Code

One command in the 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

Check with claude mcp list, or type /mcp inside a session.

Cursor

Create ~/.cursor/mcp.json (or .cursor/mcp.json in the project) with the same content as Claude Desktop above, then enable the server under Settings → MCP.

Other MCP clients

Any client that supports local (stdio) MCP servers only needs the command python3 /path/educore_mcp_bridge.py and the three environment variables above. To test without an AI client, use 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

Quick test from the terminal

Send one JSON-RPC message to the bridge; the reply lists the tools your key may use:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python3 educore_mcp_bridge.py

Available tools

ToolScopePurpose
list_schoolsroster.readSchools within the key's scope
list_classesroster.readClass groups with grade level and academic year. Filter: school_id
list_studentsroster.read
roster.pii
Students with NIS, status and current class; NISN only with roster.pii; NIK, date of birth, address and guardian contact are never sent. Filter: school_id, class_group_id, status, q (part of the name)
list_staffroster.read
roster.pii
Active staff with NIP and status; NIK only with roster.pii. Filter: school_id
list_invoicesfinance.readInvoices and balance due. Filter: status, school_id, student_id, period, due_from, due_to
get_daily_attendanceattendance.readStudent attendance for one day. Required: date (YYYY-MM-DD)
get_attendance_rangeattendance.readStudent attendance over a date range (at most 31 days). Required: date_from, date_to. Filter: school_id, student_id, status

Every tool accepts limit (at most 200) and before_id. The answer carries next_before_id; pass its value to get the next page. There are no write tools; webhook registration stays REST-only. One key may call tools at most 120 times per minute (within the 600 request limit).

If it does not work

SymptomCause and fix
The server does not appear in the clientThe file path is wrong or Python is not on PATH. Run the command from a terminal to see its error message.
SIGNATURE_INVALIDWrong secret, the key was revoked or has expired, or the computer clock is off by more than 300 seconds.
IP_NOT_ALLOWEDThe key uses an IP allow-list and your computer's address is not on it.
SCOPE_DENIEDThe key has no scope for that tool. Ask the foundation admin to issue a new key with the right scopes.
RATE_LIMITEDThe limit of 600 requests per minute per key is shared by REST and MCP.

For resellers and integrators

Every API key works for exactly one foundation; no key reaches several foundations. If you integrate many schools, ask each foundation's admin to issue you a key in the console, under Partners & API keys, with the narrowest scope possible. Keep each secret in your own vault, one per foundation.

To help with configuration inside the console, the foundation admin creates an ordinary staff login with only the roles needed. A reseller gets no cross-foundation access.

Rate limits

LimitValue
Requests per key600 / minute
Max rows per cursor page200
Fastest allowed polling interval30 seconds

Going over a limit returns 429 RATE_LIMITED with a Retry-After header.