Das gesamte Spiel ist über diese API spielbar (Web-Client & externe Bots). Basis: REST + JSON unter
/api/v1, plus WebSocket für Live-Kämpfe & Events. FastAPI liefert zusätzlich automatische OpenAPI/Swagger-Doku unter/docs. Status: Bereit für Umsetzung · 2026-07-02
https://api.seekampf.de/api/v1 (oder https://seekampf.de/api/v1).{ "error": { "code", "message" } }.Authorization: Bearer <JWT> (Browser) oder
X-API-Key: <key> (Bots). Beide gleichwertig für Spielaktionen.Idempotency-Key-Header).?limit=&offset=. Rate-Limits pro Key.POST /auth/register {name, email, password} → Account + Startinsel (Login erst nach E-Mail-Bestätigung!)
POST /auth/login {name|email, password} → {access, refresh}; 403 email_not_verified vor Bestätigung
POST /auth/refresh {refresh} → {access}
POST /auth/logout
POST /auth/verify-email {token} → E-Mail bestätigen (schaltet Login frei)
POST /auth/resend-verification {email} → Bestätigungsmail erneut senden (neutrale Antwort)
POST /auth/forgot-password {email} → Reset-Mail (SMTP, siehe ARCHITEKTUR §3a)
POST /auth/reset-password {token, new_password} → Passwort neu setzen
POST /auth/change-password {old_password, new_password} → Passwort ändern (eingeloggt)
GET /me → Profil, Punkte, Allianz, Bio
— `punkte` hier LIVE (einzige
Ausnahme, Dennis-Feedback
2026-07-06: "Live nur für
einen selber")
POST /me/profile {bio} → kurzes Spieler-Profil setzen
GET /users/{user_id}/profile → öffentliches Profil (Bio,
Punkte, Inselliste Name/
Koordinaten/Punkte/
Tageszuwachs — KEINE
Rohstoffe/Gebäude/Truppen;
zusätzlich: `online` (bool,
letzte Aktivität ≤
ONLINE_SCHWELLE_MIN=10 Min.),
`allianz` ({id,tag,name} oder
null), `quests_erfuellt`
(öffentlich sichtbare
Auszeichnungen, siehe §12).
`punkte` (Gesamt UND je Insel
in `inseln`) kommt aus dem
täglichen Tages-Snapshot,
NICHT live — gilt auch fürs
EIGENE Profil unter dieser
Route (nur `/me` ist live).
Jede Insel in `inseln` hat
zusätzlich ein `differenz`-Feld
(Punkte-Zu-/Abnahme seit
dem letzten Tages-Snapshot,
`null` vor dem ersten
Snapshot der Insel).
GET /users/lookup ?name= → wie oben, per Name gesucht
GET /me/islands → Liste eigener Inseln
POST /me/current-island {island_id} → aktive Insel wechseln
# API-Keys für Bots
GET /me/apikeys
POST /me/apikeys {label, scopes} → einmalig Klartext-Key
DELETE /me/apikeys/{id}
# DSGVO
GET /me/export → Datenexport (Auskunft, JSON)
DELETE /me {password} → Account löschen (Recht auf Vergessenwerden)
GET /legal/impressum → Impressum (HTML/Markdown)
GET /legal/datenschutz → Datenschutzerklärung
GET /legal/regeln → Spielregeln
Im Footer des Web-Clients verlinkt. Inhalte editierbar (Template/Markdown), Werte liefert Dennis.
GET /islands/{id} → Insel-Detail (Rohstoffe live, Produktion/h,
Lager, Gebäude+Stufen, Garnison, Schiffe,
laufende Aufträge, Punkte)
GET /islands/{id}/resources → aktueller Bestand + Produktion + Kapazität
GET /islands/{id}/buildings → alle Typen + Stufen
(haupthaus_ab, verfuegbar)
GET /islands/{id}/buildings/{type}/detail → Beschreibung + volle
Stufentabelle (Kosten/Zeit/
Effekt/Freischaltung)
POST /islands/{id}/buildings/{type}/upgrade → in Bau-Warteschlange (max 3)
GET /islands/{id}/construction → aktiver Bau + Queue
DELETE /islands/{id}/construction/{orderId} → Auftrag abbrechen
POST /islands/{id}/rename {name} → Insel umbenennen
Fehler wenn: Voraussetzung (Haupthaus-Stufe — Kaserne ab 5, Hafen ab 7,
Labor ab 10, Marktplatz ab 10, Steinmauer/Wachturm ab 1) fehlt, Rohstoffe
fehlen, Queue voll, Max-Stufe erreicht, oder das Gebäude deaktiviert ist
(aktuell: Marktplatz, Labor — 409 building_disabled, siehe
balancing.DEAKTIVIERTE_GEBAEUDE; Code/Daten bleiben vollständig erhalten).
POST /islands/{id}/training {facility: kaserne|hafen, item, count}
GET /islands/{id}/training → aktive Ausbildung + Queue
DELETE /islands/{id}/training/{orderId}
GET /islands/{id}/research
POST /islands/{id}/research/{tech}/upgrade
Baubarkeit prüft Kaserne-/Hafen-Stufe (z. B. Koloschiff = Hafen 20).
Forschung (speer/schild/bogen/segel/spionage/engineering, je Stufe +10 %
Effekt auf Angriff/Verteidigung/Schiffsgeschwindigkeit/Spionage-Chance/
Bauzeit) ist aktuell deaktiviert: POST /research/{tech}/upgrade →
409 feature_disabled, und research.forschung_stufen() liefert serverseitig
nur noch 0 zurück (wirkt sich also auch nirgends mehr aus) — Code/Daten
bleiben vollständig erhalten, System ist für eine Reaktivierung vorbereitet.
GET /map/sector/{x}/{y} → 5×5 Inseln des Sektors (Basis-Infos:
Besitzer/Name/Allianz/Punkte; KEINE Truppen/Rohstoffe)
GET /map/region/{x}/{y} → 3×3-Sektoren-Ausschnitt (15×15 Inseln) in
einer Antwort — fürs Ozean-Raster
GET /map/island/{x}/{y}/{z} → öffentliche Hover-Infos einer Insel; für
eingeloggte Betrachter zusätzlich
`eigene_berichte` — letzte 10, neueste
zuerst (Dennis-Feedback 2026-07-08):
eigene Angriffe auf GENAU diese Insel UND
eingehende Angriffe vom aktuellen Besitzer
dieser Insel gegen eine eigene Insel;
ungefiltert nach `Message.hidden` — im
Postfach ausgeblendete Berichte bleiben
hier trotzdem sichtbar
# Detail-Infos (Truppen/Rohstoffe) NUR via Spionage (siehe Kampfbericht)
punkte in ALLEN drei Endpunkten oben (Sektor/Region/Insel) kommt aus dem
täglichen Tages-Snapshot, NICHT live (Dennis-Feedback 2026-07-06: "in der
öffentlichen Insel-Ansicht auch") — eine brandneue Insel ohne bisherigen
Snapshot zeigt live, sonst stünde sie permanent mit 0 Punkten da.
POST /fleets {origin_island_id, mission_type, target:{x,y,z},
ships:{type:count}, units:{type:count}, resources:{...},
razzia_ziel?} (razzia_ziel nur für mission_type=attack)
→ berechnet Fahrzeit/Ankunft; validiert Ladevolumen & Kapazität
GET /fleets → eigene Flotten (outbound/returning) + Status
GET /fleets/incoming → auf eigene Inseln anfliegende Flotten,
eigene UND fremde (Sichtbarkeit/Details je
Wachturm-Stufe DER ZIELINSEL — Dennis-
Feedback 2026-07-07: auch eigene Flotten
erst innerhalb der Sichtweite, vorher immer
sichtbar) + eigene Flotten, die eine eigene
Insel gerade verlassen haben
({abfahrend: true, herkunft_insel_id,
herkunft_koordinaten, sichtbar_bis} —
sichtbar exakt solange, wie sie auch
zurückgerufen werden könnte) +
vorbeifahrende fremde Flotten
({vorbeifahrend: true, ziel_insel_id} —
nur "da fliegt was", keine weiteren Details)
POST /fleets/{id}/recall → Rückruf (nur solange in Wachturm-Sichtweite)
mission_type (Dennis-Feedback 2026-07-07): nur noch "attack" oder
"handel" — die konkrete Abwicklung (in GET /fleets als mission sichtbar:
attack/colonize/spy/transport) ergibt sich automatisch aus der Fracht:
- attack + NUR Spähschiffe an Bord (keine Einheiten/Rohstoffe) → Spionage
— geht auf JEDE existierende Insel, auch unbewohnte (eine Ruine kann noch
eine Restgarnison/Rohstoffe haben). Nur echtes leeres Wasser (kein
Inselplatz) ist kein gültiges Ziel.
- attack + Kolonisationsschiff an Bord + Ziel ist eine FREIE Insel (owner =
niemand) → Kolonisation. Ist das Ziel bewohnt, bleibt es ein normaler
Angriff — das mitgeführte Kolonisationsschiff ist dann Teil der
Eroberungs-Mechanik (gelingt die Eroberung, wird es verbraucht, genau wie
bei einer normalen Kolonisation), NICHT der Kolonisation einer freien Insel.
- attack sonst → normaler Angriff/Plünderung.
- handel → Transport: Einheiten/Rohstoffe an eine EXISTIERENDE Insel liefern
(bewohnt oder unbewohnt/Ruine — z. B. um eine Ruine als Vorposten zu
bestücken). Einheiten an eine BEWOHNTE fremde Insel nur bei eigener/Allianz-
Insel (D-Reinforce); an unbewohnte Inseln immer erlaubt. Rohstoffe an jede
existierende Insel.
Regeln: nur Einheiten+Rohstoffe transportierbar (keine Schiffe verlegen);
Rückkehr = gleiche Dauer; Beute fährt automatisch zur Heimatinsel; per handel
verschickte Einheiten gehen dauerhaft in die Garnison der Zielinsel über.
Kolonisation zielt NUR auf eine existierende, unbewohnte Insel (owner =
niemand) — leeres Wasser ohne Inselplatz ist nicht kolonisierbar. Der
Weltgenerator legt regelmäßig neue unbewohnte Inseln an (siehe ARCHITEKTUR).
razzia_ziel (Gebäudetyp) ist optional — ohne Angabe raziert der Server bei
Sieg automatisch das Haupthaus.
GET /battles/{id} → Bericht (Sichtbarkeit je Rolle:
Angreifer rudimentär / Verteidiger vollständig)
WS /ws/battles/{id} → Live-Playback-Frames (15 Min), rollenabhängig
POST /alliances {name, tag}
GET /alliances/{id} → Info, Mitglieder (inkl. Inselanzahl je
Mitglied), Diplomatie, Punkte
POST /alliances/{id}/invite {user_id} (leader/diplomat)
POST /alliances/{id}/join | /leave | /kick
POST /alliances/{id}/roles {user_id, role}
POST /alliances/{id}/kasse/deposit | /withdraw {island_id, resources}
→ {id, richtung, resources, fertig_at}
(withdraw zusätzlich: kasse-Zwischenstand)
GET /alliances/{id}/kasse/log ?limit&offset (Default 15/0, Dennis-Feedback
2026-07-09) → ABGESCHLOSSENE Ein-/
Auszahlungen (wer, wann, Richtung, Menge),
neueste zuerst — für jedes Mitglied
einsehbar. Bleibt eine reine Liste (kein
Gesamt-Zähler) — eine volle Seite
(Länge == limit) heißt "es könnte mehr
geben".
POST /alliances/{id}/diplomacy {other_alliance_id, type: krieg|buendnis|nap|neutral}
POST /alliances/{id}/broadcast {message} (Rundbrief/Chat)
GET /users/lookup ?name= → öffentliches Profil (für Einladungen; siehe §2)
Diplomatie-Semantik: krieg wirkt einseitig & sofort. buendnis/nap sind
Angebote — aktiv, sobald die Gegenseite denselben Typ setzt. neutral kündigt
Angebote/Bündnisse/NAPs sofort; ein laufender Krieg endet erst, wenn BEIDE
Seiten neutral setzen (Friedensangebot wird protokolliert).
handel-Missionen (§7) mit Einheiten sind zu einer BEWOHNTEN fremden Insel
nur bei eigener Insel/Allianzmitglied erlaubt (§2.7 D-reinforce), zu
unbewohnten Inseln immer.
Die punkte je Mitglied in der Mitgliederliste kommen aus dem täglichen
Tages-Snapshot, NICHT live (Dennis-Feedback 2026-07-03: "Live nur oben für
einen selber") — Name/Inselanzahl/Rolle bleiben live.
24h-Sperrzeit (Dennis-Feedback 2026-07-04): Sobald eine Diplomatie-
Beziehung einen settled Zustand erreicht (Krieg erklärt, Bündnis/NAP
bestätigt, Frieden geschlossen, Bündnis/NAP gekündigt), ist ein Wechsel in
einen ANDEREN Typ für ALLIANZ["diplomatie_sperre_stunden"] (Default: 24h)
gesperrt — verhindert Hin- und Her-Wechseln zwischen den Zuständen im
Minutentakt. Ein Versuch während der Sperre liefert 409 diplomacy_locked
mit dem Zeitpunkt, ab dem wieder geändert werden darf. Laufende Angebote/
Friedensverhandlungen (Status angeboten/friede_angeboten) sind von der
Sperre NICHT betroffen — Annehmen/Ablehnen/Zurückziehen geht immer sofort.
Eine gekündigte Beziehung wird intern als neutral mit Sperrfrist
gespeichert (statt gelöscht) und verschwindet aus der Diplomatie-Liste,
sobald die Sperre abgelaufen ist.
Kasse-Transferzeit (Dennis-Feedback 2026-07-06): Ein-/Auszahlung wirken
NICHT mehr sofort, sondern erst nach ALLIANZ["kasse_transferzeit_h"]
(Default 2h, unter Turbo TURBO_FAKTOR-mal kürzer wie überall sonst).
Einzahlung: Rohstoffe werden SOFORT von der Insel abgezogen, kommen aber
erst nach der Transferzeit in der Kasse an. Auszahlung: der Betrag wird
SOFORT aus der Kasse gebucht (reserviert, kein Doppel-Ausgeben), kommt aber
erst nach der Transferzeit auf der Zielinsel an — Lagerkapazität wird ERST
BEI ANKUNFT geprüft (kann sich in 2h ändern), was dann nicht passt, geht
zurück in die Kasse. GET /alliances/{id} liefert für Mitglieder zusätzlich
kasse_unterwegs: [{id, richtung, resources, spieler, fertig_at}] — alle
noch nicht abgeschlossenen Transfers. GET .../kasse/log zeigt weiterhin
nur ABGESCHLOSSENE Bewegungen (mit der tatsächlich angekommenen Menge, bei
Auszahlung ggf. weniger als angefordert wegen Lagerkapazität).
GET /market/offers ?want=&offer= → Angebote filtern
POST /market/offers {offer_res, offer_amount, want_res, want_amount}
POST /market/offers/{id}/accept {from_island_id} → erzeugt Transport-Fleet
DELETE /market/offers/{id}
# Freier (riskanter) Versand:
POST /transfers {from_island_id, target:{x,y,z}, resources}
# Handelsrouten (wiederkehrend, eigene Inseln):
GET/POST/DELETE /trade-routes POST: {from_island_id, to_island_id,
resources, interval_h}
Marktplatz/Marktangebote sind aktuell deaktiviert (POST /islands/{id}/
buildings/marktplatz/upgrade → 409 building_disabled; POST /market/offers
→ 409 feature_disabled) — Endpunkte, Code und Daten bleiben vollständig
erhalten, siehe balancing.DEAKTIVIERTE_GEBAEUDE. Markt-Semantik (F16b-
Defaults, Werte in balancing.py → MARKT), falls/wenn reaktiviert: Angebot
braucht Marktplatz ≥ 1; angebotene Menge + Gebühr wird beim Einstellen
hinterlegt (Escrow — das Verhältnis ist abgesichert). Kapazität je Angebot/
Annahme = 500 × Marktplatz-Stufe; max. Angebote je Insel = 2 + Stufe;
Gebühr = 10 % − 0,4 %/Stufe. Lieferungen fahren als abstrakte Markt-
Handelsschiffe (4 Knoten, echte Fahrzeit) — keine eigenen Schiffe nötig.
Zurückziehen/Ablauf erstattet die Menge, die Gebühr verfällt.
Freier Versand (/transfers) nutzt EIGENE Handelsschiffe (automatisch
gewählt, größte zuerst) — ohne jede Absicherung. Handelsrouten liefern mit
eigenen Handelsschiffen; fehlen Schiffe/Rohstoffe, wird der Lauf übersprungen.
Beide sind AKTIV, unabhängig vom deaktivierten Marktplatz.
GET /messages ?folder=inbox|combat
POST /messages {recipient, subject, body}
POST /messages/{id}/read
DELETE /messages/{id}
DELETE /messages/{id}: bei folder=combat (Kampf-/Spionageberichte) NUR ein
Soft-Hide (hidden=true, verschwindet aus GET /messages) — Kampfberichte
werden nie wirklich gelöscht, sie bleiben für spätere Statistiken im System
(GRUNDBEDINGUNGEN §2.6b). Bei folder=inbox (normale Nachrichten) echtes
Löschen der Zeile.
GET /forum/threads ?sichtbarkeit=oeffentlich|allianz (optional; ohne
Parameter beide, je nach Sichtbarkeitsregel gemischt)
&limit=&offset=
POST /forum/threads {titel, body, sichtbarkeit?: "oeffentlich"|"allianz"}
sichtbarkeit-Default: "oeffentlich"
GET /forum/threads/{id} ?limit=&offset= → Thema + alle Beiträge
POST /forum/threads/{id}/posts {body} → Antwort auf ein Thema
POST /forum/posts/{id}/report → melden (jeder eingeloggte Spieler)
DELETE /forum/posts/{id} (nur is_admin)
DELETE /forum/threads/{id} (nur is_admin)
Ein "unfassbar vereinfachtes" In-Game-Brett (Dennis-Feedback 2026-07-05) — kein Editieren, keine Kategorien/Unterforen, nur Themen + Antworten. Nur eingeloggt erreichbar (kein öffentlicher HTTP-Endpunkt wie bei §2a).
Sichtbarkeit: jedes Thema ist "oeffentlich" (für alle sichtbar) oder
"allianz" (nur für Mitglieder GENAU dieser Allianz + Admin). Ein
allianzinternes Thema anlegen ohne eigene Allianz → 403 not_member. Zugriff
auf ein fremdes Allianz-Thema (View/Antworten/Melden) → 404 not_found (keine
Info-Preisgabe über die Existenz, analog zu fremden Inseln). Admins sehen und
moderieren alle Themen unabhängig von eigener Allianz-Mitgliedschaft.
Moderation: POST /forum/posts/{id}/report ist idempotent (mehrfaches
Melden durch denselben Spieler erzeugt keinen zweiten Eintrag) und für jeden
eingeloggten Spieler erlaubt — nur klein sichtbar im Frontend, kein
Sofort-Effekt. Die Felder gemeldet (Themenliste) und melde_anzahl
(Beitrag-Detail) kommen von der API nur mit, wenn der Aufrufer is_admin
ist — normale Spieler sehen nie, ob/wie oft etwas gemeldet wurde. Admin
kann jeden Beitrag oder jedes ganze Thema löschen (protokolliert im
Admin-Audit-Log); löscht man den letzten Beitrag eines Themas, verschwindet
das dann leere Thema automatisch mit.
Link-Verbot: Titel und Beitragstext (Thema wie Antwort) werden serverseitig
gegen eine Link-Heuristik geprüft (Protokoll-URLs, www., wortartige
Domain.TLD-Muster ohne Leerzeichen) — bei Treffer 422 link_verboten. Eine
Heuristik, kein vollständiger Parser; erkennt nicht jede Verschleierung, löst
aber nicht bei normalen Satzenden aus.
GET /rankings/players → nach Gesamtpunkten
GET /rankings/alliances → nach Punkten
GET /stats/overview → öffentlich, KEIN Login nötig — Server-Kennzahlen
(Spieler/Inseln/Allianzen/Flotten unterwegs),
Top-10-Spieler/Top-5-Allianzen, weltweiter
Einheiten-/Schiffsbestand (Häfen) UND was
gerade auf See ist (Schiffe + Einheiten in
Flotten). Auch als Seite unter /statistik
(HTML, von der Startseite verlinkt).
GET /quests → Tutorial + Meilensteine + heutige Dailies + Fortschritt
POST /quests/{id}/claim → Belohnung (Rohstoffe) abholen
Rangliste live vs. eingefroren (Dennis-Feedback 2026-07-03): Rang/Punkte/
Differenz kommen aus dem täglichen Snapshot und ändern sich erst beim
nächsten Tageswechsel. Name/Inseln/Allianz werden dagegen bei JEDEM
Abruf frisch aus der Datenbank nachgeladen (live) — ein Allianzwechsel oder
gelöschter Account ist also sofort sichtbar, nur die Punktzahl/der Rang
selbst bleibt bis Mitternacht fix. Gelöschte Spieler/Allianzen werden aus
dem Snapshot entfernt, sobald sie beim nächsten Abruf nicht mehr existieren.
GET /rankings/players liefert außerdem online: bool je Spieler (Dennis-
Feedback 2026-07-08) — ebenfalls live (letzte Aktivität < ONLINE_SCHWELLE_MIN
Minuten), analog zum online-Feld im öffentlichen Profil (§ Nutzer); die
genaue letzte Aktivität selbst bleibt privat, nur der Bool wird ausgeliefert.
Tageswechsel = Mitternacht in der Server-Zeitzone Europe/Berlin (DST-
sicher via app.core.timeutil.server_datum()), NICHT UTC-Mitternacht — Fix
2026-07-05, vorher wechselte der Snapshot erst 1–2h nach der deutschen
Mitternacht, was wie ein Berechnungsfehler wirkte.
Öffentliche Auszeichnungen (Dennis-Feedback 2026-07-04): quests_erfuellt
im öffentlichen Profil (§2) zeigt NUR Quests aus einer fest hinterlegten
Whitelist, die nichts über die eigene Insel oder deren Ausbaustufen verrät
(z. B. Allianz beigetreten, Kampf gewonnen, Insel erobert, geplündert,
spioniert, Kontoalter, sowie die drei Lieferungs-/Kolonisations-Quests unten)
— Gebäude-/Forschungs-/Punkte-Quests bleiben privat.
Meilensteine M13–M15 (neu, Dennis-Feedback 2026-07-03/04): - M13 Truppentransport — 1.000 Steinewerfer in EINER Lieferung verschickt (Maximum je Einzellieferung zählt). - M14 Großhändler — 100.000 Rohstoffe INSGESAMT verschickt (Summe über alle Lieferungen — nicht auf einmal, bewusst andere Semantik als M13). - M15 Gescheiterte Landung — 100 gescheiterte Kolonisationsversuche erlebt.
Dailies-Pool auf 15 Aufgaben erweitert (balancing.DAILIES["pool"]); pro Tag
werden weiterhin 3 davon angeboten, deterministisch per HMAC(Server-Seed,
Datum) ausgewählt — für alle Spieler am selben Tag garantiert identisch.
Interne Admin-/Testhilfe-Endpunkte (nur is_admin) — bewusst NICHT Teil
dieser öffentlichen API-Dokumentation und aus der automatischen OpenAPI/
Swagger-Doku (/docs) ausgeschlossen (Dennis-Feedback 2026-07-08).
WS /ws → Benachrichtigungen (Auftrag fertig, Flotte
angekommen, Angriff im Anflug, neue Nachricht)
WS /ws/battles/{id} → Live-Kampf-Playback
Auth beim WS-Handshake via ?token= (JWT) oder ?api_key=.
Viele Antworten sehen je nach Rolle/Status/Admin-Recht unterschiedlich aus —
Felder werden nicht nur mit null befüllt, sondern weggelassen. Diese
Referenz listet ALLE Varianten, damit ein Bot nicht raten muss, welche Felder
auftauchen können. (Dennis-Feedback 2026-07-06: "was alles für Informationen
übertragen werden könnte", damit ein Bot genau weiß, was passieren kann.)
Message.payload (Postfach, §11) — je nach NachrichtentypKampfbericht — Angreifer, Sieg:
{
"battle_id": 42, "rolle": "angreifer", "sieg": true,
"insel_name": "Nebelinsel", "insel_koordinaten": "12:34:5",
"eingesetzt": {"angreifer": {"units": {}, "ships": {}},
"verteidiger": {"units": {}, "ships": {}}},
"verluste": {"angreifer": {}, "verteidiger": {}},
"razzia": {"goldmine": 1},
"loot": {"gold": 120, "stein": 40},
"erobert": false,
"aufklaerung": null,
"aufklaerung_status": "kein_spaehschiff",
"rng": {"eroberung": {"wurf": 0.71, "schwelle": 0.5}},
"text_voll": "…"
}
insel_name/insel_koordinaten (Dennis-Feedback 2026-07-08) stehen in JEDER
Kampfbericht-Variante (Angreifer Sieg/Niederlage, Verteidiger) sowie in
GET /battles/{id} — Inselnamen sind ohnehin überall öffentlich sichtbar
(GET /map/island), keine zusätzliche Info-Preisgabe.
aufklaerung ist entweder null oder ein Schnappschuss (siehe 14a.2).
aufklaerung_status: "kein_spaehschiff" (kein Spähschiff dabei) |
"erfolgreich" | "fehlgeschlagen" (Würfel nicht getroffen). rng-Keys
variieren je nachdem, welche Zufallsentscheidungen im Kampf gefallen sind
(angriffsaufklaerung, eroberung, …), jeweils {wurf, schwelle}.
aufklaerung.rohstoffe wird NACH der Plünderung erfasst (Dennis-Feedback
2026-07-07) — zeigt bei erfolgreicher Zusatz-Aufklärung also den Bestand
ABZÜGLICH loot, nicht den Stand vor dem Angriff.
Kampfbericht — Angreifer, Niederlage (Dennis-Feedback 2026-07-09: "auch
ohne Spähschiff Informationen über den Gegner ... wie der Kampf lief" —
eingesetzt/verluste beider Seiten sind jetzt AUCH bei Niederlage sichtbar,
nicht mehr nur bei Sieg):
{
"battle_id": 42, "rolle": "angreifer", "sieg": false,
"insel_name": "Nebelinsel", "insel_koordinaten": "12:34:5",
"eingesetzt": {"angreifer": {"units": {}, "ships": {}},
"verteidiger": {"units": {}, "ships": {}}},
"verluste": {"angreifer": {}, "verteidiger": {}},
"verteidiger_name": "SpielerName",
"text_voll": "…"
}
Kein razzia/loot/aufklaerung (die gibt's nur bei Sieg) — die
Zusatz-Aufklärung (Gebäude/Rohstoffe/Schiffsbestand der Insel) bleibt
weiterhin exklusiv an Spähschiff + Sieg gebunden, NUR der reine
Kampfverlauf (wer mit wieviel eingesetzt/verloren hat) ist jetzt immer
sichtbar. verteidiger_name ist null, wenn das Ziel unbewohnt war.
Kampfbericht — Verteidiger (immer vollständig, unabhängig vom Ausgang):
{
"battle_id": 42, "rolle": "verteidiger", "sieg": true,
"insel_name": "Nebelinsel", "insel_koordinaten": "12:34:5",
"eingesetzt": {"angreifer": {}, "verteidiger": {}},
"verluste": {"angreifer": {}, "verteidiger": {}},
"razzia": {}, "loot": {}, "angreifer": "SpielerName", "text_voll": "…"
}
Spionage — Ergebnis versenkt:
{"typ": "spionage", "ergebnis": "versenkt", "ziel": "12:34:5",
"insel_name": "Nebelinsel",
"rng": {"chance": 0.3, "wurf": 0.81, "entdeckt": true, "versenkt": true}}
insel_name (Dennis-Feedback 2026-07-08) steht in allen drei
typ: "spionage"-Varianten (versenkt/erfolg/fehlschlag) — NICHT in den
spionage_abwehr-Varianten (die betreffen die eigene Insel des Empfängers,
der kennt sie bereits).
Spionage-Abwehr — Ziel wurde ausspioniert, Späher versenkt:
{"typ": "spionage_abwehr", "ergebnis": "versenkt", "angreifer": "SpielerName"}
Spionage — Erfolg:
{"typ": "spionage", "ergebnis": "erfolg", "ziel": "12:34:5",
"bericht": {"info_tiefe": 3, "rohstoffe": {"gold": 900, "stein": 400, "holz": 200},
"gebaeude": {"haupthaus": 5}, "truppen": {"steinewerfer": 20}},
"rng": {"chance": 0.6, "wurf": 0.22}}
bericht ist nach info_tiefe gestaffelt — Felder kommen ERST ab der
jeweiligen Tiefe dazu (niedrigere Tiefe = weniger Felder, nicht null):
info_tiefe 1: nur rohstoffe. ≥2: zusätzlich gebaeude. ≥3: zusätzlich
truppen. ≥4: zusätzlich schiffe, forschung.
Spionage — Fehlschlag (kein bericht-Feld):
{"typ": "spionage", "ergebnis": "fehlschlag", "ziel": "12:34:5", "rng": {...}}
Spionage-Abwehr — nur gesichtet (Angreifer bleibt anonym, kein angreifer-Feld):
{"typ": "spionage_abwehr", "ergebnis": "gesichtet"}
Allianz-Einladung: {"alliance_id": 3, "invite": true}
Allianz-Rundbrief: {"alliance_id": 3, "rundbrief": true}
aufklaerung und Spionage-bericht verwendet){"rohstoffe": {"gold": 900, "stein": 400, "holz": 200},
"gebaeude": {"haupthaus": 5, "goldmine": 3},
"truppen": {"steinewerfer": 20}, "schiffe": {"spaehschiff": 1},
"forschung": {"segel": 2}}
Welche Top-Level-Keys vorhanden sind, hängt vom Kontext ab (Aufklärung durch Spähschiff im Angriff vs. gestufter Spionagebericht, siehe oben).
Solange now < battle.end_at, wird JEDES Kampf-/Spionage-Payload (egal
welche Variante oben) beim Abruf über GET /messages/GET /messages/{id}
auf genau diese 4 Felder reduziert — der volle Body/Payload kommt erst nach
Kampfende:
{"battle_id": 42, "rolle": "angreifer", "fertig": false,
"end_at": "2026-07-06T14:32:00Z"}
GET /battles/{id} ist vor Kampfende analog gesperrt:
{"id": 42, "start_at": "...", "end_at": "...", "fertig": false,
"hinweis": "Der Kampf läuft noch — voller Bericht ab Kampfende abrufbar.",
"rolle": "angreifer"}
GET /battles/{id} — volle Ansicht nach Kampfende (§8)Alle Varianten haben zusätzlich insel_name/insel_koordinaten (§14a.1).
Verteidiger (immer voll): id, start_at, end_at, rolle:"verteidiger",
ergebnis, timeline, eingesetzt, verluste, razzia, loot, conquered,
angreifer_name.
Angreifer + Sieg: id, start_at, end_at, rolle:"angreifer", ergebnis,
timeline, eingesetzt, verluste, razzia, loot, conquered, aufklaerung,
aufklaerung_status, rng, verteidiger_name.
Angreifer + Niederlage (Dennis-Feedback 2026-07-09, NICHT mehr
rudimentär — nur die Zusatz-Aufklärung fehlt): id, start_at, end_at,
rolle:"angreifer", ergebnis, timeline, eingesetzt, verluste,
verteidiger_name, hinweis. Kein razzia/loot/conquered/aufklaerung
(die gibt's nur bei Sieg). verteidiger_name ist null bei unbewohntem Ziel.
timeline-Einträge (phase: "see"|"land") tragen seit 2026-07-09 zusätzlich
angriffsstaerke/verteidigungsstaerke (die tatsächlich verglichenen Werte,
die über Sieg/Niederlage entscheiden — VOR den Verlusten). Der "land"-
Eintrag trägt außerdem basis_inselverteidigung (0 oder KAMPF
["basis_inselverteidigung"]=50 — 50 gilt bereits ab 1 verteidigender
Einheit, 0 nur bei einer komplett leeren Zielinsel). Ältere, vor diesem
Datum gespeicherte Battles haben diese Felder nicht (fehlen einfach, kein
null).
WS /ws — genau 4 Event-Typen (kein weiterer){"typ": "verbunden"}
{"typ": "nachricht", "id": 7, "folder": "inbox", "subject": "Hallo"}
{"typ": "flotte_ankunft", "fleet_id": 12, "state": "outbound|returning|done"}
{"typ": "angriff_im_anflug", "ziel": "12:34:5", "mission": "attack",
"arrive_at": "2026-07-06T14:00:00Z"}
WS /ws/battles/{id} — Live-Playback-FramesDrei Phasen nacheinander, je mit eigener Zeitscheibe (KAMPF["phase_anteile"]):
see (Seegefecht) → land (Landkampf) → belagerung (Razzia/Gebäudezerstörung,
nur wenn der Angreifer gewinnt). Ein Feld erscheint in JEDEM Frame ab dem
Zeitpunkt, an dem es das erste Mal relevant wird (nicht erst während seiner
eigenen Phase) — Werte VOR ihrer Phase = Ausgangsbestand, NACH ihrer Phase =
Endbestand (eingefroren), NUR während ihrer eigenen Phase wird interpoliert.
{"t": 45, "fortschritt": 0.3, "phase": "land",
"angreifer_schiffe": {"kleines_kriegsschiff": 4},
"angreifer_einheiten": {"bogenschuetze": 12}}
angreifer_schiffe/verteidiger_schiffe erscheinen NUR, wenn die jeweilige
Seite tatsächlich Schiffe eingesetzt hat (der Verteidiger also NUR mit
stationierten Kriegsschiffen) — schickt der Angreifer Schiffe gegen eine
Insel ohne gegnerische Marine, laufen sie unbehelligt ein und bleiben trotzdem
sichtbar (keine Verluste, aber kein leeres Feld).
Rollenabhängig — der Angreifer bekommt verteidiger_schiffe/
verteidiger_einheiten NIE (nicht nur null, komplett weggelassen), der
Verteidiger sieht beide Seiten. Beim letzten Frame (fortschritt >= 1) kommt
zusätzlich ergebnis: "attacker_win"|"defender_win" dazu — für den
Verteidiger immer, für den Angreifer NUR bei attacker_win (bei eigener
Niederlage bleibt ergebnis im Live-Stream weg, genau wie im Bericht).
Während der belagerung-Phase (nur bei Angreifer-Sieg) zusätzlich, für BEIDE
Rollen sichtbar (keine Truppen-/Schiffs-Info, daher keine Geheimhaltung):
{"t": 270, "fortschritt": 0.9, "phase": "belagerung",
"belagerung_ziel": "haupthaus", "belagerung_stufe": 2}
belagerung_stufe zählt live vom Ausgangswert auf den im Kampfbericht
protokollierten Endwert (razzia) herunter.
GET /alliances/{id} (§9): kasse, rolle und kasse_unterwegs
erscheinen NUR, wenn der Aufrufer selbst Mitglied ist. Nicht-Mitglieder
sehen nur id, name, tag, beschreibung, gegruendet, punkte, mitglieder[],
diplomatie[].GET /forum/threads (§11a): gemeldet: bool je Thema NUR für Admins.GET /forum/threads/{id} (§11a): melde_anzahl: int je Beitrag NUR
für Admins.Angriff starten: (Typ-Schlüssel sind IMMER Deutsch, siehe balancing.SCHIFFE/
EINHEITEN — z. B. kleines_kriegsschiff, nicht small_warship)
POST /api/v1/fleets
X-API-Key: sk_live_...
{
"origin_island_id": 12,
"mission_type": "attack",
"target": { "x": 20, "y": 8, "z": 20 },
"ships": { "kleines_kriegsschiff": 10, "spaehschiff": 1, "kleines_handelsschiff": 5 },
"units": { "bogenschuetze": 80, "steinewerfer": 40 },
"razzia_ziel": "goldmine"
}
→ 201 {
"id": 88, "mission": "attack", "origin_island_id": 12,
"target": { "x": 20, "y": 8, "z": 20 },
"ships": { "kleines_kriegsschiff": 10, "spaehschiff": 1, "kleines_handelsschiff": 5 },
"units": { "bogenschuetze": 80, "steinewerfer": 40 }, "resources": {}, "loot": {},
"state": "outbound", "speed_knots": 10, "distanz_felder": 42,
"depart_at": "2026-07-02T18:20:00Z", "arrive_at": "2026-07-02T18:40:00Z",
"return_at": null, "recallable_until": "2026-07-02T18:30:00Z"
}
mission im Response ist die vom Server aus der Fracht ABGELEITETE konkrete
Mission (hier attack, da weder nur Spähschiffe noch ein Kolonisationsschiff
an Bord sind) — siehe §7.