Tocco-Mate exponiert eine schlanke REST-API. Alle Endpoints (außer /healthz) erfordern einen Bearer-Token im Authorization-Header.
curl -H "Authorization: Bearer $API_TOKEN" \
http://localhost:3000/api/noten
$API_TOKEN findest du nach dem ersten Start in data/.api-token oder in den Logs (AUTO-GENERATED API TOKEN).
| Methode | Pfad | Beschreibung |
|---|
GET | /healthz | Health-Check (kein Auth) |
| Methode | Pfad | Beschreibung |
|---|
GET | /api/status | Scheduler- und Server-Status |
| Methode | Pfad | Beschreibung |
|---|
GET | /api/settings | Settings lesen |
PATCH | /api/settings | Settings ändern (validiert + persistiert) |
| Methode | Pfad | Beschreibung |
|---|
GET | /api/noten | Noten (?semester=S1&sortBy=note) |
GET | /api/noten/:id/pruefungen | LB / ZP / OTHER eines Moduls |
GET | /api/history/:id | Notenverlauf eines Moduls |
GET | /api/stats | Gesamt-Statistiken |
| Methode | Pfad | Beschreibung |
|---|
GET | /api/stundenplan | Termine (?from=YYYY-MM-DD&limit=100) |
POST | /api/stundenplan/clear | Alle Stundenplan-Einträge löschen |
| Methode | Pfad | Beschreibung |
|---|
POST | /api/scrape | Manuellen Scrape auslösen |
| Methode | Pfad | Beschreibung |
|---|
GET | /api/events | SSE-Stream für Live-Status (akzeptiert ?token=…) |
GET | /api/logs | Letzte Log-Zeilen |
| Methode | Pfad | Beschreibung |
|---|
GET | /api/push/vapid-key | VAPID-Public-Key der PWA |
POST | /api/push/subscribe | Push-Subscription registrieren |
DELETE | /api/push/subscribe | Push-Subscription entfernen |
POST | /api/push/test | Test-Push an alle Subscriptions |
Alle Endpoints antworten mit JSON. Standard-Envelope:
"data": { /* Payload */ },
Bei Fehlern:
"message": "Invalid or missing bearer token"
curl -H "Authorization: Bearer $API_TOKEN" \
"http://localhost:3000/api/noten?semester=S1&sortBy=note"
curl -H "Authorization: Bearer $API_TOKEN" \
"http://localhost:3000/api/noten/M114/pruefungen"
const es = new EventSource('/api/events?token=' + API_TOKEN);
const data = JSON.parse(e.data);
console.log('event:', data.type, data.payload);
Event-Typen: scrape:start, scrape:phase, scrape:done, note:new, note:changed, room:changed.
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.subscribe({
applicationServerKey: urlBase64ToUint8Array(vapidPublicKey),
await fetch('/api/push/subscribe', {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
body: JSON.stringify(sub),
| Endpoint | Limit |
|---|
/api/* (Auth-Fehler) | 10 / 15 min, 50 / 6 h |
/api/events (Auth-Fehler) | 60 / 15 min |
/api/scrape | Implicit (single-flight) — gleichzeitige Aufrufe werden serialisiert |
Siehe Sicherheit.
Default: kein CORS-Header — die API ist same-origin only (vom Dashboard / Mobile-PWA aus).
Wenn du externe Clients brauchst, ergänze einen CORS-Middleware-Hook in src/server.js.