API-Grundlagen
Alle Endpunkte deiner Community liegen unter https://<deine-community>/api/…
und antworten mit JSON.
Authentifizierung
Es gibt keine API-Schlüssel. Authentifiziert wird über ein HttpOnly-Session-Cookie, das beim Anmelden gesetzt wird:
- Das Cookie ist
HttpOnly,SecureundSameSite— JavaScript kommt nicht daran, und es reist nicht ungewollt zu fremden Seiten mit. - Requests aus dem Browser müssen es mitschicken (
credentials: 'include'beifetch, sofern nicht ohnehin same-origin). - Ein Server-zu-Server-Zugang für Drittsysteme existiert derzeit nicht.
Ohne gültige Sitzung antworten geschützte Routen mit 401, ohne ausreichende
Rechte mit 403.
Fehler-Format
Fehler unterhalb von /api/ kommen in einem stabilen Umschlag zurück —
gedacht dafür, dass du gegen feste Codes programmieren kannst statt gegen
HTTP-Status zu raten:
{ "ok": false, "code": "VALIDATION_ERROR", "message": "targetId and targetType are required" }
| Code | HTTP | Bedeutung |
|---|---|---|
VALIDATION_ERROR | 400, 422 | Eingabe unvollständig oder ungültig |
UNAUTHORIZED | 401 | Keine (gültige) Sitzung |
FORBIDDEN | 403 | Angemeldet, aber nicht berechtigt |
NOT_FOUND | 404 | Nicht vorhanden — oder für dich nicht sichtbar |
CONFLICT | 409 | Zustand passt nicht (z. B. Adresse bereits belegt) |
RATE_LIMITED | 429 | Zu viele Anfragen |
INTERNAL_ERROR | ab 500 | Serverfehler |
Ab 500 ist die message bewusst generisch: interne Details, Stacktraces und
Datenbank-Meldungen verlassen den Server nicht.
404 steht auch dann, wenn etwas existiert, aber nicht zu deiner Community
gehört. Das ist Absicht — die Antwort soll nicht verraten, dass es die Zeile
anderswo gibt.Rate-Limits
Gezählt wird pro IP in einem Fenster von einer Minute:
| Bereich | Grenze pro Minute |
|---|---|
| Anmeldung, Code- und Passwort-Zurücksetzung | 5 |
Kommentare lesen (GET /api/comments, GET /api/comments/count) | 120 |
| Schreiben (Kommentar, Beitrag, Vote, Meldung) | 60 |
| Gast-Kommentare | 5 |
Ist die Grenze erreicht, kommt 429 mit code: "RATE_LIMITED". Bei Anmeldung
und Code-Prüfung zählen nur fehlgeschlagene Versuche — ein erfolgreicher
Login verbraucht das Budget nicht.
Cross-Origin
Nur ein Endpunkt ist bewusst von fremden Seiten aus abrufbar:
GET /api/comments/count?targetId=<id>&targetType=<typ>
→ { "count": 12 }
Er antwortet mit Access-Control-Allow-Origin: *, ist read-only, kommt ohne
Cookies aus und enthält keine personenbezogenen Daten. Genau diesen Endpunkt
nutzt embed.js für data-pukalani-count (siehe
Embed-Widget).
Alle übrigen Endpunkte sind same-origin. Kommentare auf einer fremden Seite
darstellen geht deshalb über das iframe-Widget, nicht über eigene fetch-Aufrufe.
Kommentare lesen (same-origin)
GET /api/comments?targetId=<id>&targetType=<typ>&sort=new&page=1
| Parameter | Pflicht | Werte |
|---|---|---|
targetId | ja | Schlüssel des Strangs |
targetType | ja | Namensraum, z. B. blog |
sort | nein | new (Default), top, trending, discussed |
page | nein | Seitenzahl ab 1 |
Paginiert wird über Top-Level-Stränge; jeder wird mit seinem kompletten Antwortbaum geliefert, damit keine Antwort ohne ihren Ausgangsbeitrag ankommt. Ausgeblendete Kommentare fehlen; vom Autor gelöschte bleiben als Platzhalter erhalten, damit der Strang lesbar bleibt.
Erreichbarkeit prüfen
GET /api/health
→ { "ok": true, "user": null, "build": "<commit>" }
Beantwortet auch HEAD — Uptime-Monitore und Load-Balancer prüfen oft so.
Echtzeit
Neue und geänderte Kommentare erreichen offene Browser über einen WebSocket, den die Oberfläche selbst aufbaut und authentifiziert. Eine öffentliche, dokumentierte Realtime-Schnittstelle für Fremdsysteme gibt es nicht — im eingebetteten Widget ist die Echtzeit-Aktualisierung aber ohne Zutun enthalten.