External Open API v1
CSMS External Open API (v1)
Applies to: CSMS v0.3.2+
Base path:/api/v1/**
Companion doc: the internal admin API is described in Internal API; this file only covers the external third-party endpoints.
1. Overview & Three Iron Rules
The External Open API lets third-party systems (campus card, academic platform, auto-seating scripts) read/write school / grade / class business data within an authorized scope, without touching system-level data (accounts, onboarding, announcements, mail services).
Three Iron Rules
- System-level data (admins, schools onboarding, announcements, mail) is never operable via the Open API.
- Write operations are limited to school-level and below business data.
- Every request must carry a valid
api_token.
2. Base Conventions
2.1 Base URL
https://<your-csms-domain>/api/v12.2 Unified Response Envelope
{
"code": 0,
"message": "success",
"data": { },
"requestId": "req_xxxx"
}| Field | Type | Notes |
|---|---|---|
code | int | 0 = success; non-zero = error (see §5) |
message | string | Human-readable message |
data | any | Payload; may be null |
requestId | string | Correlates with the audit log |
2.3 Pagination
Query params page (default 1) and limit (default 20, max 100). Response data includes items and pagination (total, page, pageSize, totalPages).
2.4 Sort Whitelist
Only whitelisted fields may be used in sortBy; direction via sortOrder (asc / desc). Unknown fields are ignored.
2.5 Time Fields & Date Filters
All timestamps are UTC ISO 8601 (2026-01-01T00:00:00Z). Date filters use startDate / endDate (inclusive, UTC).
3. Authentication
3.1 Carrying the token
Send the credential in either header:
Authorization: Bearer <api_token>
# or
X-API-Token: <api_token>Tokens are stored as sha256 hashes; the plaintext is shown only once at issuance.
3.2 Validation order (short-circuit)
- Token present? → else
40101 TOKEN_MISSING - Token format valid? → else
40102 TOKEN_INVALID - Token exists & enabled? → else
40103 TOKEN_DISABLED/40104 TOKEN_EXPIRED - Issuer enabled? → else
40105 ISSUER_DISABLED - School enabled? → else
40301 SCHOOL_DISABLED - Scope & permission satisfied? → else
40302 SCOPE_DENIED/40303 OUT_OF_RANGE
3.3 Credential issuance (admin side)
Issued in the admin panel /admin/api-tokens. Requires re-authentication. Copy the plaintext immediately — it is never shown again.
4. Scope & Permissions
4.1 Three scope types
| Scope | Meaning |
|---|---|
school | Whole school |
grade | A specific grade |
class | A specific class |
4.2 Twelve permissions
| Permission | Description | Dangerous |
|---|---|---|
students:read | Read students | |
students:write | Create / update students | |
students:delete | Delete students | 🔴 |
scores:read | Read scores | |
scores:write | Add / update score records | |
scores:revoke | Revoke score records | |
structure:read | Read grades / classes | |
structure:write | Create / update grades / classes | |
structure:delete | Delete grades / classes | 🔴 |
templates:read | Read score templates | |
templates:write | Create / update templates | |
stats:read | Read statistics |
Dangerous permissions (students:delete, structure:delete) are off by default.
4.3 Scope × Permission
Access requires both a matching scope (horizontal) and a granted permission (vertical) — an AND relationship.
4.4 Read 404 vs Write 403
To prevent enumeration: a missing resource returns 40401 NOT_FOUND on read, but 40302 SCOPE_DENIED on write when the target is outside the token's scope.
5. Error Codes
| code | name | meaning |
|---|---|---|
0 | OK | success |
40001 | BAD_REQUEST | malformed request |
40002 | VALIDATION_FAILED | field validation failed |
40003 | INVALID_PARAM | invalid parameter |
40004 | UNSUPPORTED | unsupported operation |
40101 | TOKEN_MISSING | token not provided |
40102 | TOKEN_INVALID | token malformed |
40103 | TOKEN_DISABLED | token disabled |
40104 | TOKEN_EXPIRED | token expired |
40105 | ISSUER_DISABLED | token issuer disabled |
40301 | SCHOOL_DISABLED | target school disabled |
40302 | SCOPE_DENIED | permission/scope not satisfied |
40303 | OUT_OF_RANGE | target outside token scope |
40401 | NOT_FOUND | resource not found |
40901 | CONFLICT | conflict (e.g. duplicate) |
42901 | RATE_LIMITED | rate limit exceeded |
50001 | INTERNAL | internal server error |
6. Rate Limit
- Total: 600 requests / minute per token.
- Writes: 120 requests / minute per token.
- Exceeding returns
42901with aRetry-Afterheader (seconds).
7. Idempotency
Send an Idempotency-Key header (max 128 chars) on write requests. Retrying with the same key yields the original result instead of a duplicate (concurrent conflict returns 40901). Keys are stored in the per-school api_idempotency table.
8. Call Audit
Every v1 call — including auth failures and rate-limit hits — is logged to api_audit_logs with 9 fields (token id, issuer, school, ip, path, method, status, latency, X-Request-Id). Retention: 30 days.
9. Endpoint Catalog (22 external endpoints)
9.1 Connectivity
GET /ping— health check
9.2 Students (/students)
GET /students— listGET /students/:id— get onePOST /students— createPATCH /students/:id— updateDELETE /students/:id— delete (needsstudents:delete)
9.3 Scores (/scores)
GET /scores— listPOST /scores— addDELETE /scores/:id— revoke (needsscores:revoke)
9.4 Grades (/grades)
GET /grades— listPOST /grades— createPATCH /grades/:id— updateDELETE /grades/:id— delete (needsstructure:delete)
9.5 Classes (/classes)
GET /classes— listPOST /classes— createPATCH /classes/:id— updateDELETE /classes/:id— delete (needsstructure:delete)
9.6 Score Templates (/templates)
GET /templates— listPOST /templates— createPATCH /templates/:id— updateDELETE /templates/:id— delete
9.7 Statistics (/stats)
GET /stats/overview— overview (needsstats:read)
10. Credential Management (internal, /api/api-tokens)
Six endpoints for issuing / listing / updating / disabling / revoking credentials and viewing logs, all requiring admin Session auth (not the Open API token). Issuance and revocation require re-authentication.
11. Quick Start (curl)
# Health check
curl https://<domain>/api/v1/ping \
-H "Authorization: Bearer $TOKEN"
# List students (page 1, 20 per page)
curl "https://<domain>/api/v1/students?page=1&limit=20" \
-H "Authorization: Bearer $TOKEN"
# Add a score (idempotent)
curl -X POST https://<domain>/api/v1/scores \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"studentId":"...","amount":2,"reason":"Excellent homework"}'Node.js (fetch):
const res = await fetch("https://<domain>/api/v1/students", {
headers: { Authorization: `Bearer ${TOKEN}` },
});
const json = await res.json();
console.log(json.code, json.data);12. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
40101 | no token | add Authorization: Bearer |
40104 | token expired | re-issue credential |
40302 / 40303 | scope/permission mismatch | check token scope & granted perms |
40401 on read | resource missing | verify id |
40901 | duplicate write | reuse Idempotency-Key or change payload |
42901 | rate limited | back off using Retry-After |
13. Compatibility & Versioning
- Base path
/api/v1/**is versioned; breaking changes ship under a new version prefix. - Clients should pin to
v1and watchcode/messagefor deprecation notices.
