Für Entwickler
Für Ihre eigenen Skripte, Bots und Tools.
Server abfragen, starten und stoppen, Befehle an die Konsole senden, Dateien lesen und schreiben, Automationen anlegen, die Ihnen signierte Webhooks schicken, Backups anlegen, Rechnungen abrufen. Ein Token legen Sie im Kundenbereich unter Konto, API-Zugang an; es kann nie mehr als Sie selbst.
Einstieg
In drei Schritten zur ersten Antwort.
Im Kundenbereich unter Konto, API-Zugang: einen Namen, die Berechtigungen und auf Wunsch einzelne Server wählen. Das Token beginnt mit gf_ und wird nur dieses eine Mal gezeigt.
Am besten als Umgebungsvariable, damit es nicht im Skript selbst steht:
export GF_TOKEN="gf_…"Listet Ihre Server mit Name und Zustand. Jede Anfrage trägt das Token im Header Authorization.
curl https://api.goodflare.io/api/v1/servers \
-H "Authorization: Bearer $GF_TOKEN"Authorization: Bearer gf_…. Nur so: ein angemeldeter Browser hat hier ohne Token keinen Zugriff.Jeder Fehler kommt als JSON: error ist ein fester Code, den Ihre Skripte prüfen können, message ein Satz für Menschen.
| Status | error | Bedeutung |
|---|---|---|
| 400 | invalid_signalinvalid_commandinvalid_pathinvalid_nameinvalid_bodynot_a_filesame_folderinto_itselfno_webhook | Die Anfrage passt nicht: ein unbekanntes signal, ein leerer oder zu langer Befehl, ein unzulässiger Pfad oder Name, ein Ordner statt einer Datei, ein Body, der kein JSON-Objekt ist. |
| 401 | unauthenticatedinvalid_token | Kein Token, oder es ist ungültig, abgelaufen oder widerrufen. |
| 403 | missing_scopemissing_right | Dem Token fehlt die Berechtigung für diesen Endpunkt, oder Ihnen auf einem geteilten Server das Recht, das ein Schritt einer Automation braucht. |
| 404 | not_found | Nicht gefunden oder für dieses Token nicht sichtbar; auch ein VPS bei Endpunkten, die es nur für Gameserver gibt. |
| 409 | suspendednot_readynot_runninglimitexistsbin_unavailablebin_failedbusy | Der Server ist gesperrt, noch nicht bereit, läuft nicht (bei Befehlen), sein Backup- oder Automationen-Limit ist erreicht, ein Name gibt es im Ziel schon, der Papierkorb nimmt etwas nicht auf, oder er ist gerade mit etwas anderem beschäftigt. |
| 413 | too_large | Die Anfrage ist größer als 16 KB (eine Firewall oder Adressliste größer als 512 KB), eine Datei als Text größer als 1 MB, ein Upload größer als 8 MB. |
| 415 | binary_file | Die Datei ist keine Textdatei: über /files/download laden. |
| 422 | invalid_automation | Die Automation passt nicht: der Grund steht in message, wie im Editor des Kundenbereichs. |
| 429 | rate_limitedbusytoo_fast | Zu viele Anfragen. Bei rate_limited und busy sagt Retry-After, wann es wieder geht; too_fast heißt: pro Server nur eine Power-Aktion alle paar Sekunden. |
| 500 | server_error | Ein Fehler auf unserer Seite. Bleibt er, schreiben Sie uns gern mit der Uhrzeit der Anfrage. |
| 501 | unsupported | Dieser Server unterstützt die Aktion nicht. |
| 502 | panel_error | Der Server oder sein Host hat die Aktion nicht ausgeführt oder nicht geantwortet. |
| 504 | timeout | Nach 15 Sekunden noch keine Antwort. Die Aktion kann trotzdem laufen: Prüfen Sie den Zustand, statt sie zu wiederholen. |
Beispiel: einem Token fehlt die Berechtigung „control“
{
"error": "missing_scope",
"message": "Dem Token fehlt die Berechtigung „control“."
}Endpunkte
GET/api/v1/servers
Eigene und mit Ihnen geteilte Server; hat das Token eine Serverliste, nur diese. statusText ist der Zustand in Worten, bei einem angehaltenen Server mit dem Grund (stopReason: unpaid, ended, withdrawn oder staff; deleteOn, wann ein gekündigter gelöscht wird).
Berechtigung: Lesen (read)
curl https://api.goodflare.io/api/v1/servers \
-H "Authorization: Bearer $GF_TOKEN"{
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"product": "Minecraft · Standard",
"type": "game",
"game": "minecraft",
"os": null,
"status": "active",
"statusText": "Aktiv",
"stopReason": null,
"deleteOn": null,
"role": "owner",
"nextDueAt": "2026-11-28T09:14:03.000Z",
"deleteProtection": false,
"inRescue": false,
"addons": [
{
"key": "backups",
"name": "Mehr Backup-Plätze",
"units": 1,
"endsAt": null
}
],
"createdAt": "2026-10-28T09:14:03.000Z"
}
]
}GET/api/v1/servers/{id}
Zustand, Laufzeit, Prozessor, Arbeitsspeicher, Speicher, Netzwerk und Adressen, wie auf der Übersicht; dazu, ob der Löschschutz an ist (deleteProtection) und ob ein VPS im Rettungssystem läuft (inRescue); addons sind die gebuchten Zusatzpakete (key, name, units: wie viele davon; endsAt, wenn eins gekündigt ist und dann endet, mit keepsUnits: wie viele danach bleiben), nur zum Lesen. Kündigen, Neuinstallieren und Löschen gibt es über die API nicht.
Berechtigung: Lesen (read)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"server": {
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"product": "Minecraft · Standard",
"type": "game",
"game": "minecraft",
"os": null,
"status": "active",
"statusText": "Aktiv",
"stopReason": null,
"deleteOn": null,
"role": "owner",
"nextDueAt": "2026-11-28T09:14:03.000Z",
"deleteProtection": false,
"inRescue": false,
"addons": [
{
"key": "backups",
"name": "Mehr Backup-Plätze",
"units": 1,
"endsAt": null
}
],
"createdAt": "2026-10-28T09:14:03.000Z",
"live": {
"state": "running",
"uptimeSeconds": 86400,
"cpu": {
"percent": 18.5,
"limitPercent": 300
},
"memory": {
"bytes": 3328599654,
"limitBytes": 6442450944
},
"disk": {
"bytes": 4939212390,
"limitBytes": 42949672960
},
"network": {
"rxBytes": 512000000,
"txBytes": 1380000000
},
"addresses": [
{
"label": "Adresse",
"value": "203.0.113.24:25565"
}
],
"blocker": null
}
}
}POST/api/v1/servers/{id}/power
Wie die Knöpfe im Kundenbereich. Pro Server höchstens eine Aktion alle paar Sekunden. Auf einem mit Ihnen geteilten Server braucht es dort das Recht „Ein/Aus“.
Berechtigung: Server steuern (control)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| signalPflicht | Body | string | start, stop, restart oder kill |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/power \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"signal":"restart"}'{
"ok": true
}POST/api/v1/servers/{id}/command
Eine Zeile an die Konsole eines laufenden Gameservers, als wäre sie dort getippt. Höchstens 30 pro Minute und Server. Auf einem mit Ihnen geteilten Server braucht es dort die Rechte „Ein/Aus“ und „Konsole“.
Berechtigung: Server steuern (control)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| commandPflicht | Body | string | eine Zeile, höchstens 1000 Zeichen |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/command \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"command":"say Neustart in 5 Minuten"}'{
"ok": true
}GET/api/v1/servers/{id}/backups
Die Backups eines Gameservers und wie viele sein Tarif erlaubt.
Berechtigung: Backups (backups)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/backups \
-H "Authorization: Bearer $GF_TOKEN"{
"backups": [
{
"id": "4f1c2a9e-7b3d-4c8a-9e21-5d6f0b8a3c17",
"name": "Vor dem Update",
"bytes": 1874329600,
"createdAt": "2026-10-28T08:00:02.000Z",
"completedAt": "2026-10-28T08:01:37.000Z",
"ok": true,
"locked": false
}
],
"limit": 5
}POST/api/v1/servers/{id}/backups
Startet ein Backup; es ist fertig, wenn es in der Liste completedAt hat.
Berechtigung: Backups (backups)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| name | Body | string | optional, sonst Datum und Uhrzeit |
| locked | Body | boolean | optional, vor dem Löschen geschützt |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/backups \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Vor dem Update"}'{
"ok": true,
"name": "Vor dem Update"
}Endpunkte
GET/api/v1/servers/{id}/files
Dateien und Ordner an einem Pfad eines Gameservers. Der Papierkorb des Servers wird nicht gelistet.
Berechtigung: Dateien ansehen (files.read)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| path | Query | string | der Ordner, Standard / |
curl "https://api.goodflare.io/api/v1/servers/SERVER_ID/files?path=/plugins" \
-H "Authorization: Bearer $GF_TOKEN"{
"path": "/plugins",
"files": [
{
"name": "Essentials",
"type": "directory",
"size": 4096,
"modifiedAt": "2026-10-27T18:02:11+00:00"
},
{
"name": "EssentialsX-2.21.2.jar",
"type": "file",
"size": 3260416,
"modifiedAt": "2026-10-27T18:01:54+00:00"
}
]
}GET/api/v1/servers/{id}/files/content
Der Inhalt einer Textdatei, höchstens 1 MB. Größere oder binäre Dateien (415) laden Sie über /files/download.
Berechtigung: Dateien ansehen (files.read)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| pathPflicht | Query | string | die Datei |
curl "https://api.goodflare.io/api/v1/servers/SERVER_ID/files/content?path=/server.properties" \
-H "Authorization: Bearer $GF_TOKEN"{
"path": "/server.properties",
"size": 1342,
"modifiedAt": "2026-10-27T18:02:11+00:00",
"content": "motd=A Minecraft Server\nmax-players=20\n…"
}PUT/api/v1/servers/{id}/files/content
Ersetzt den Inhalt einer Textdatei, oder legt sie mit ihren Ordnern an; höchstens 1 MB. Der Body ist der neue Inhalt als Text, oder ein JSON-Objekt mit dem Feld content. Steht in der Aktivität des Servers.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| pathPflicht | Query | string | die Datei |
| textPflicht | Body | text/plain | der neue Inhalt als Text |
curl -X PUT "https://api.goodflare.io/api/v1/servers/SERVER_ID/files/content?path=/server.properties" \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: text/plain" \
--data-binary @server.properties{
"ok": true,
"path": "/server.properties",
"size": 1351
}GET/api/v1/servers/{id}/files/download
Ein signierter Link direkt zum Server, 15 Minuten gültig, für Dateien jeder Größe, wie im Dateimanager. Der Link selbst braucht kein Token: Geben Sie ihn nicht weiter.
Berechtigung: Dateien ansehen (files.read)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| pathPflicht | Query | string | die Datei |
curl "https://api.goodflare.io/api/v1/servers/SERVER_ID/files/download?path=/world.zip" \
-H "Authorization: Bearer $GF_TOKEN"{
"path": "/world.zip",
"size": 734003200,
"url": "https://node1.goodflare.io:8080/download/file?token=eyJhbGciOi…",
"expiresAt": "2026-10-29T14:15:00.000Z"
}POST/api/v1/servers/{id}/files/upload
Die Datei selbst als Body, bis 8 MB; eine gleichnamige wird ersetzt. Größere Dateien über /files/upload-url.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| path | Query | string | der Ordner, Standard / |
| namePflicht | Query | string | der Dateiname |
| bytesPflicht | Body | application/octet-stream | die Datei selbst |
curl -X POST "https://api.goodflare.io/api/v1/servers/SERVER_ID/files/upload?path=/plugins&name=EssentialsX.jar" \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @EssentialsX.jar{
"ok": true,
"path": "/plugins/EssentialsX.jar",
"size": 3260416
}POST/api/v1/servers/{id}/files/upload-url
Ein signierter Link direkt zum Server, 15 Minuten gültig, bis zur Grenze des Servers (maxBytes): Senden Sie die Dateien als multipart/form-data im Feld files per POST an url. Der Ordner steht schon im Link.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| path | Body | string | der Ordner, Standard / |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/upload-url \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"path":"/world"}'{
"path": "/world",
"url": "https://node1.goodflare.io:8080/upload/file?token=eyJhbGciOi…&directory=%2Fworld",
"field": "files",
"maxBytes": 104791040,
"expiresAt": "2026-10-29T14:15:00.000Z"
}POST/api/v1/servers/{id}/files/rename
Eine Datei oder einen Ordner, oder mit files bis zu 50 auf einmal, der Reihe nach. Scheitert einer, bleibt umbenannt, was schon umbenannt ist, und die Antwort (207) sagt welche.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| root | Body | string | der Ordner, Standard / |
| from | Body | string | der alte Name |
| to | Body | string | der neue Name |
| files | Body | array | statt from und to: bis zu 50 Paare {from, to} |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/rename \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"root":"/plugins","from":"config.yml","to":"config.old.yml"}'{
"ok": true,
"renamed": [
{
"from": "config.yml",
"to": "config.old.yml"
}
]
}POST/api/v1/servers/{id}/files/move
Bis zu 50 Dateien oder Ordner in einen anderen, schon vorhandenen Ordner. Gibt es einen Namen im Ziel schon, wird nichts verschoben (409 exists).
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| root | Body | string | wo sie liegen, Standard / |
| namesPflicht | Body | array | die Namen, 1 bis 50 |
| destinationPflicht | Body | string | der Zielordner |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/move \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"root":"/","names":["old-world"],"destination":"/archive"}'{
"ok": true,
"moved": [
"old-world"
],
"destination": "/archive"
}POST/api/v1/servers/{id}/files/folder
Ein neuer Ordner.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| root | Body | string | wo er entsteht, Standard / |
| namePflicht | Body | string | sein Name |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/folder \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"root":"/plugins","name":"backups"}'{
"ok": true,
"path": "/plugins/backups"
}POST/api/v1/servers/{id}/files/delete
Bis zu 50 Dateien oder Ordner. Wie im Dateimanager kommen sie in den Papierkorb des Servers (7 Tage, dort wiederherstellbar). Nimmt der Papierkorb sie nicht auf (er ist aus, oder sie sind größer als seine Grenze), wird nichts gelöscht und die Antwort ist 409 bin_unavailable, es sei denn, permanent ist true.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| root | Body | string | wo sie liegen, Standard / |
| namesPflicht | Body | array | die Namen, 1 bis 50 |
| permanent | Body | boolean | endgültig löschen, ohne Papierkorb |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/delete \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"root":"/logs","names":["2026-10-01-1.log.gz"]}'{
"ok": true,
"mode": "bin",
"deleted": [
"2026-10-01-1.log.gz"
],
"binEntry": "2026-10-29T13-58-02-4f1c2a"
}POST/api/v1/servers/{id}/files/compress
Ein .tar.gz neben den gewählten Dateien; den Namen vergibt der Server. Bei großen Ordnern kann die Antwort nach 15 Sekunden 504 sein, während das Packen weiterläuft.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| root | Body | string | wo sie liegen, Standard / |
| namesPflicht | Body | array | die Namen, 1 bis 50 |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/compress \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"root":"/","names":["world"]}'{
"ok": true,
"archive": "/archive-2026-10-29T135802.tar.gz"
}POST/api/v1/servers/{id}/files/decompress
Ein Archiv (.zip, .tar.gz, .tar und weitere) in seinen Ordner. Gleichnamige Dateien werden überschrieben.
Berechtigung: Dateien bearbeiten (files.write)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| root | Body | string | wo es liegt, Standard / |
| namePflicht | Body | string | das Archiv |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/files/decompress \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"root":"/","name":"world.zip"}'{
"ok": true
}Endpunkte
Dieselben Felder wie im Editor unter Automationen. Ein Feld, das zum Ereignis oder zur Art eines Schritts nicht passt, wird ignoriert.
| Wert | Beschreibung |
|---|---|
| line | eine Zeile in der Konsole · match: text, mode (contains oder regex), caseSensitive; {line} und bei regex eine Gruppe (?<player>…) für {player} |
| started | der Server ist gestartet |
| stopped | der Server ist ausgegangen |
| crashed | der Server ist abgestürzt (erkannt in ein bis zwei Minuten) |
| join | ein Spieler tritt bei · match: player (optional); nur Minecraft |
| leave | ein Spieler geht · match: player (optional); nur Minecraft |
| players | die Spielerzahl · match: op (atLeast oder atMost), count, minutes; nur Minecraft |
| called | nur, wenn eine andere Automation sie ausführt |
| Wert | Felder |
|---|---|
| command | command: eine Zeile an die Konsole, mit {player} und {line} |
| power | signal: start, restart oder stop |
| backup | keine Felder: ein Backup wie aus einem Zeitplan |
| notify | message, channels: mail, discord, whatsapp, push |
| webhook | url (https, öffentlich) oder urlRef (eine gespeicherte), message (optional); siehe Webhooks |
| schedule | scheduleId: ein Zeitplan des Servers |
| wait | seconds: 1 bis 600, zusammen höchstens 1800 |
| fire | automationId der anderen als triggerId: sie ausführen |
| enable · disable | triggerId: eine andere ein- (enable) oder ausschalten (disable) |
onlyIf: {"kind": "on" oder "off", "triggerId": …} (eine andere ist ein- oder ausgeschaltet), oder {"kind": "nobody"} bzw. "somebody" (Minecraft).
GET/api/v1/servers/{id}/automations
Die Automationen eines Gameservers, wie unter Automationen im Kundenbereich, mit limit (wie viele er haben kann) und signingKey (wann der Signaturschlüssel entstand, sonst null). Aufbau einer Automation: siehe oben.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/automations \
-H "Authorization: Bearer $GF_TOKEN"{
"automations": [
{
"id": "Hq7mWz2LcX9pRk4T",
"name": "Absturz melden",
"enabled": true,
"paused": false,
"event": "crashed",
"match": {},
"onlyIf": null,
"steps": [
{
"kind": "webhook",
"urlRef": "v1.Zb3…",
"urlHint": "example.com/…",
"format": "json"
}
],
"cooldown": 300,
"failures": 0,
"lastError": null,
"lastRunAt": "2026-10-29T11:42:08.000Z"
}
],
"limit": 25,
"signingKey": {
"createdAt": "2026-10-28T09:20:11.000Z"
}
}POST/api/v1/servers/{id}/automations
Eine neue Automation, geprüft wie im Editor des Kundenbereichs; passt etwas nicht, antwortet die API mit 422 und dem Grund. Mit dem ersten Webhook-Schritt des Servers entsteht sein Signaturschlüssel und steht dieses eine Mal in signingSecret.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| name | Body | string | optional, höchstens 80 Zeichen; sonst ein Satz aus dem Ereignis |
| eventPflicht | Body | string | was sie auslöst |
| match | Body | object | die Einzelheiten des Ereignisses |
| onlyIf | Body | object | optional, eine Bedingung |
| stepsPflicht | Body | array | die Schritte, 1 bis 10 |
| cooldown | Body | integer | Sekunden zwischen zwei Ausführungen, 5 bis 86400, Standard 60 |
| enabled | Body | boolean | Standard true |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/automations \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Absturz melden","event":"crashed","steps":[{"kind":"webhook","url":"https://example.com/hooks/goodflare"}],"cooldown":300}'{
"automation": {
"id": "Hq7mWz2LcX9pRk4T",
"name": "Absturz melden",
"enabled": true,
"paused": false,
"event": "crashed",
"match": {},
"onlyIf": null,
"steps": [
{
"kind": "webhook",
"urlRef": "v1.Zb3…",
"urlHint": "example.com/…",
"format": "json"
}
],
"cooldown": 300,
"failures": 0,
"lastError": null,
"lastRunAt": "2026-10-29T11:42:08.000Z"
},
"signingSecret": "whsec_3q2vT0c…"
}GET/api/v1/servers/{id}/automations/{automationId}
Eine Automation mit ihrem Zustand: paused, wenn das Portal sie nach 5 Fehlern in Folge ausgeschaltet hat, lastError und lastRunAt.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| automationIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/AUTOMATION_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"automation": {
"id": "Hq7mWz2LcX9pRk4T",
"name": "Absturz melden",
"enabled": true,
"paused": false,
"event": "crashed",
"match": {},
"onlyIf": null,
"steps": [
{
"kind": "webhook",
"urlRef": "v1.Zb3…",
"urlHint": "example.com/…",
"format": "json"
}
],
"cooldown": 300,
"failures": 0,
"lastError": null,
"lastRunAt": "2026-10-29T11:42:08.000Z"
}
}PATCH/api/v1/servers/{id}/automations/{automationId}
Die gesendeten Felder ersetzen die bisherigen, die anderen bleiben. Nur enabled zu senden schaltet sie ein oder aus; Einschalten gibt einer pausierten einen neuen Anfang. Ein Webhook-Schritt behält seine Adresse, wenn Sie ihn mit urlRef unverändert zurückschicken.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| automationIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| enabled | Body | boolean | ein- oder ausschalten |
| name | Body | string | wie beim Anlegen |
| steps | Body | array | wie beim Anlegen, die ganze Liste |
curl -X PATCH https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/AUTOMATION_ID \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled":false}'{
"automation": {
"id": "Hq7mWz2LcX9pRk4T",
"name": "Absturz melden",
"enabled": false,
"paused": false,
"event": "crashed",
"match": {},
"onlyIf": null,
"steps": [
{
"kind": "webhook",
"urlRef": "v1.Zb3…",
"urlHint": "example.com/…",
"format": "json"
}
],
"cooldown": 300,
"failures": 0,
"lastError": null,
"lastRunAt": "2026-10-29T11:42:08.000Z"
}
}DELETE/api/v1/servers/{id}/automations/{automationId}
Mit ihren Ausführungen und Zustellungen. Schritte anderer Automationen, die sie ausführen, werden danach übersprungen.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| automationIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl -X DELETE https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/AUTOMATION_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"ok": true
}GET/api/v1/servers/{id}/automations/{automationId}/runs
Was sie getan hat, die neueste zuerst, 30 Tage lang: result ok, failed oder skipped, mit Anlass und Ergebnis in Worten.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| automationIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| limit | Query | integer | 1 bis 100, Standard 30 |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/AUTOMATION_ID/runs \
-H "Authorization: Bearer $GF_TOKEN"{
"runs": [
{
"id": "pV3nL8xQ2mT7rK1z",
"at": "2026-10-29T11:42:08.000Z",
"result": "ok",
"cause": "Absturz erkannt",
"detail": "Webhook zugestellt (200)"
}
]
}GET/api/v1/servers/{id}/automations/{automationId}/deliveries
Was ihre Webhook-Schritte gesendet haben, die neueste zuerst, 30 Tage lang: status (sending, ok, retrying, failed), attempts, responseCode, durationMs und die ersten 300 Zeichen der Antwort. Kam sie nicht an, sagt error warum: timeout, unreachable, refused, http oder gone. Die Adresse steht nur als Hinweis (target) darin.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| automationIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| limit | Query | integer | 1 bis 100, Standard 30 |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/AUTOMATION_ID/deliveries \
-H "Authorization: Bearer $GF_TOKEN"{
"deliveries": [
{
"id": "dlv_8mQ2xV7nT4kL9pR3wZ1c",
"automationId": "Hq7mWz2LcX9pRk4T",
"step": 1,
"type": "server.crashed",
"test": false,
"target": "example.com/…",
"format": "json",
"status": "ok",
"attempts": 1,
"responseCode": 200,
"durationMs": 184,
"response": "{\"received\":true}",
"error": null,
"nextAttemptAt": null,
"lastAttemptAt": "2026-10-29T11:42:08.000Z",
"createdAt": "2026-10-29T11:42:08.000Z"
}
]
}POST/api/v1/servers/{id}/automations/{automationId}/test
Eine Zustellung vom Typ webhook.test an jeden Webhook-Schritt (höchstens 3), oder nur an Schritt step; wie „Test senden“ im Editor. Höchstens 6 pro Minute und Server.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| automationIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| step | Body | integer | optional, nur dieser Schritt (ab 1) |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/AUTOMATION_ID/test \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"step":1}'{
"deliveries": [
{
"id": "dlv_8mQ2xV7nT4kL9pR3wZ1c",
"automationId": "Hq7mWz2LcX9pRk4T",
"step": 1,
"type": "webhook.test",
"test": true,
"target": "example.com/…",
"format": "json",
"status": "ok",
"attempts": 1,
"responseCode": 200,
"durationMs": 184,
"response": "{\"received\":true}",
"error": null,
"nextAttemptAt": null,
"lastAttemptAt": "2026-10-29T11:42:08.000Z",
"createdAt": "2026-10-29T11:42:08.000Z"
}
]
}POST/api/v1/servers/{id}/automations/signing-key
Ein neuer Schlüssel für die Signatur aller Webhooks dieses Servers, nur in dieser Antwort zu sehen. Der bisherige gilt sofort nicht mehr.
Berechtigung: Automationen (automations)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/automations/signing-key \
-H "Authorization: Bearer $GF_TOKEN"{
"signingSecret": "whsec_3q2vT0c…",
"createdAt": "2026-10-29T14:00:00.000Z"
}Webhooks
Ein Schritt „Webhook senden“ schickt bei jedem Ereignis eine Anfrage an Ihre Adresse: im Kundenbereich unter Automationen, oder über die API mit steps: [{"kind": "webhook", "url": "https://…"}]. Ein Discord-Webhook bekommt eine Discord-Nachricht; jede andere https-Adresse bekommt JSON wie unten, signiert.
Ein POST mit JSON. In Version 1 bleiben die Felder, wie sie sind; neue kommen höchstens hinzu. Prüfen Sie type und version, nicht die Zahl der Felder.
POST /hooks/goodflare HTTP/1.1
Content-Type: application/json
User-Agent: goodflare-portal/1.0 (+https://goodflare.io)
X-GoodFlare-Event: player.joined
X-GoodFlare-Delivery: dlv_8mQ2xV7nT4kL9pR3wZ1c
X-GoodFlare-Attempt: 1
X-GoodFlare-Signature: t=1793297051,v1=6e1c0b9f2a…{
"type": "player.joined",
"version": 1,
"id": "dlv_8mQ2xV7nT4kL9pR3wZ1c",
"createdAt": "2026-10-29T18:04:11.204Z",
"test": false,
"server": {
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden"
},
"automation": {
"id": "Hq7mWz2LcX9pRk4T",
"name": "Willkommen",
"step": 2
},
"event": {
"kind": "join",
"player": "Steve",
"line": "[18:04:11 INFO]: Steve joined the game"
},
"text": "Wenn ein Spieler beitritt: Steve ist beigetreten",
"message": "Willkommen, Steve!"
}| Name | Beschreibung |
|---|---|
| type | was passiert ist, siehe die Tabelle darunter |
| version | immer 1 in dieser Version |
| id | die Zustellung; bleibt bei Wiederholungen gleich (auch im Header X-GoodFlare-Delivery), so erkennen Sie ein doppeltes Ankommen |
| createdAt | wann es passiert ist (UTC) |
| test | true bei „Test senden“ |
| server | id und name des Servers |
| automation | id, name und step (der Schritt, ab 1); null bei einem Test aus einer noch nicht gespeicherten Automation |
| event | kind (das Ereignis), player und line, wo es sie gibt, sonst null |
| text | der Satz, was passiert ist, in der Sprache des Serverbesitzers |
| message | die Nachricht des Schritts mit {player} und {line} gefüllt, oder null |
| console.line | eine Zeile in der Konsole passte |
| server.started | der Server ist gestartet |
| server.stopped | der Server ist ausgegangen |
| server.crashed | der Server ist abgestürzt |
| player.joined | ein Spieler ist beigetreten |
| player.left | ein Spieler ist gegangen |
| players.changed | die Spielerzahl hat die Grenze erreicht |
| automation.called | eine andere Automation hat diese ausgeführt |
| webhook.test | „Test senden“ oder POST …/test |
X-GoodFlare-Signature: t=<Unix-Sekunden>,v1=<hex>. v1 ist HMAC-SHA256 über t, einen Punkt und den Body, genau so, wie er ankam (nicht neu serialisiert), mit dem Signaturschlüssel des Servers. Lehnen Sie ab, wenn v1 nicht passt oder t mehr als fünf Minuten abweicht: So nützt ein mitgeschnittener Aufruf niemandem. Den Schlüssel sehen Sie einmal, wenn er entsteht: mit dem ersten Webhook-Schritt, oder neu über POST …/automations/signing-key. Discord-Nachrichten tragen keine Signatur.
// Node.js 18 oder neuer, ohne weitere Pakete
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(secret, header, rawBody, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=", 2)));
const t = Number(parts.t);
if (!Number.isInteger(t) || !/^[0-9a-f]{64}$/.test(parts.v1 ?? "")) return false;
if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
return timingSafeEqual(expected, Buffer.from(parts.v1, "hex"));
}
// mit Express: den Body roh lesen, nicht als JSON
app.post("/hooks/goodflare", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(process.env.GF_WEBHOOK_SECRET, req.get("X-GoodFlare-Signature") ?? "", raw)) return res.sendStatus(401);
const event = JSON.parse(raw);
console.log(event.type, event.server.name, event.text);
res.sendStatus(204);
});Endpunkte
GET/api/v1/firewalls
Die Firewalls Ihres Kontos: gemeinsame (ownerServerId null) und die eigenen einzelner Server (ownerServerId: der Server), mit Richtlinien, Zahl der Regeln und den Servern, auf denen sie gelten. limits sind die Grenzen Ihres Kontos (firewalls, lists, listEntries, rules, stackRules, entriesPerServer): die Standardgrenzen, sofern wir auf Ihre Anfrage keine höheren eingetragen haben; mehr fragen Sie im Kundenbereich auf der Seite Firewalls an. Nur mit einem Token für alle Server.
Berechtigung: Firewalls (firewall)
curl https://api.goodflare.io/api/v1/firewalls \
-H "Authorization: Bearer $GF_TOKEN"{
"firewalls": [
{
"id": "2q2bflmo9po6",
"name": "Webserver",
"ownerServerId": null,
"policyIn": "DROP",
"policyOut": "ACCEPT",
"ruleCount": 3,
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"state": "applied"
}
],
"updatedAt": "2026-10-30T15:48:09.000Z"
}
],
"limits": {
"firewalls": 20,
"lists": 20,
"listEntries": 1000,
"rules": 50,
"stackRules": 100,
"entriesPerServer": 3000
}
}POST/api/v1/firewalls
Eine neue Firewall, noch an keinem Server; geprüft wie im Editor des Kundenbereichs (422 mit dem Grund). Sperrt sie SSH (Port 22) aus, braucht es allowNoSsh: true (sonst 409).
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| namePflicht | Body | string | Name, bis 60 Zeichen |
| policyInPflicht | Body | string | ACCEPT, DROP oder REJECT: was keine eingehende Regel trifft |
| policyOutPflicht | Body | string | ACCEPT, DROP oder REJECT, ausgehend |
| rulesPflicht | Body | array | die Regeln in ihrer Reihenfolge, die erste passende entscheidet; je Regel dir (in, out), action (ACCEPT, DROP, REJECT), proto (any, tcp, udp, icmp, icmpv6, gre, esp, ah), port ("443", "8000-8100", "80,443"), icmpType, peer ({"kind":"any"}, {"kind":"cidr","cidrs":[…]}, {"kind":"list","id":…}, {"kind":"feed","key":"spamhaus-drop"}), note, on |
| allowNoSsh | Body | boolean | optional, true: SSH bewusst aussperren |
| logDrops | Body | boolean | optional, true: Verworfenes wird protokolliert (Tab Firewall) |
curl -X POST https://api.goodflare.io/api/v1/firewalls \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Webserver","policyIn":"DROP","policyOut":"ACCEPT","rules":[{"dir":"in","action":"ACCEPT","proto":"tcp","port":"22,80,443","peer":{"kind":"any"},"note":"SSH, Web"}]}'{
"id": "2q2bflmo9po6",
"name": "Webserver",
"revision": 1,
"policyIn": "DROP",
"policyOut": "ACCEPT",
"rules": [
{
"id": "5cbat01mng",
"dir": "in",
"action": "ACCEPT",
"proto": "tcp",
"port": "22",
"icmpType": "",
"peer": {
"kind": "any"
},
"note": "SSH",
"on": true
},
{
"id": "5cbat02axe",
"dir": "in",
"action": "ACCEPT",
"proto": "tcp",
"port": "80,443",
"icmpType": "",
"peer": {
"kind": "any"
},
"note": "HTTP, HTTPS",
"on": true
},
{
"id": "5cbat04072",
"dir": "in",
"action": "ACCEPT",
"proto": "icmp",
"port": "",
"icmpType": "echo-request",
"peer": {
"kind": "any"
},
"note": "Ping",
"on": true
}
],
"servers": [],
"updatedAt": "2026-10-30T15:48:09.000Z"
}GET/api/v1/firewalls/{firewallId}
Richtlinien, Regeln, Revision und die Server, an denen sie hängt.
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| firewallIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl https://api.goodflare.io/api/v1/firewalls/FIREWALL_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"id": "2q2bflmo9po6",
"name": "Webserver",
"revision": 3,
"policyIn": "DROP",
"policyOut": "ACCEPT",
"rules": [
{
"id": "5cbat01mng",
"dir": "in",
"action": "ACCEPT",
"proto": "tcp",
"port": "22",
"icmpType": "",
"peer": {
"kind": "any"
},
"note": "SSH",
"on": true
},
{
"id": "5cbat02axe",
"dir": "in",
"action": "ACCEPT",
"proto": "tcp",
"port": "80,443",
"icmpType": "",
"peer": {
"kind": "any"
},
"note": "HTTP, HTTPS",
"on": true
},
{
"id": "5cbat04072",
"dir": "in",
"action": "ACCEPT",
"proto": "icmp",
"port": "",
"icmpType": "echo-request",
"peer": {
"kind": "any"
},
"note": "Ping",
"on": true
}
],
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"state": "applied",
"appliedRevision": 3
}
],
"updatedAt": "2026-10-30T15:48:09.000Z"
}PUT/api/v1/firewalls/{firewallId}
Die ganze Firewall neu (Felder wie beim Anlegen, name optional). Sie wird im Hintergrund auf alle ihre Server geschrieben (202); wie weit, sagt GET /servers/{id}/firewall. Der vorige Stand bleibt für Rückgängig im Kundenbereich.
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| firewallIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| name | Body | string | optional |
| policyInPflicht | Body | string | ACCEPT, DROP oder REJECT |
| policyOutPflicht | Body | string | ACCEPT, DROP oder REJECT |
| rulesPflicht | Body | array | die Regeln in ihrer Reihenfolge, die erste passende entscheidet; je Regel dir (in, out), action (ACCEPT, DROP, REJECT), proto (any, tcp, udp, icmp, icmpv6, gre, esp, ah), port ("443", "8000-8100", "80,443"), icmpType, peer ({"kind":"any"}, {"kind":"cidr","cidrs":[…]}, {"kind":"list","id":…}, {"kind":"feed","key":"spamhaus-drop"}), note, on |
| allowNoSsh | Body | boolean | optional |
| logDrops | Body | boolean | optional, true: Verworfenes wird protokolliert (Tab Firewall) |
curl -X PUT https://api.goodflare.io/api/v1/firewalls/FIREWALL_ID \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"policyIn":"DROP","policyOut":"ACCEPT","rules":[{"dir":"in","action":"ACCEPT","proto":"tcp","port":"22","peer":{"kind":"any"},"note":"SSH"}]}'{
"id": "2q2bflmo9po6",
"name": "Webserver",
"revision": 4,
"policyIn": "DROP",
"policyOut": "ACCEPT",
"rules": [
{
"id": "5cbat01mng",
"dir": "in",
"action": "ACCEPT",
"proto": "tcp",
"port": "22",
"icmpType": "",
"peer": {
"kind": "any"
},
"note": "SSH",
"on": true
},
{
"id": "5cbat02axe",
"dir": "in",
"action": "ACCEPT",
"proto": "tcp",
"port": "80,443",
"icmpType": "",
"peer": {
"kind": "any"
},
"note": "HTTP, HTTPS",
"on": true
},
{
"id": "5cbat04072",
"dir": "in",
"action": "ACCEPT",
"proto": "icmp",
"port": "",
"icmpType": "echo-request",
"peer": {
"kind": "any"
},
"note": "Ping",
"on": true
}
],
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"state": "pending"
}
],
"updatedAt": "2026-10-30T15:48:09.000Z"
}DELETE/api/v1/firewalls/{firewallId}
Nur, wenn sie an keinem Server mehr hängt (sonst 409).
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| firewallIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl -X DELETE https://api.goodflare.io/api/v1/firewalls/FIREWALL_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"ok": true
}GET/api/v1/firewalls/lists
IP-Listen (entries: Adressen und Netze) und AS-Listen (entries: AS-Nummern, prefixCount: wie viele Präfixe sie gerade ankündigen) Ihres Kontos.
Berechtigung: Firewalls (firewall)
curl https://api.goodflare.io/api/v1/firewalls/lists \
-H "Authorization: Bearer $GF_TOKEN"{
"lists": [
{
"id": "k3vcv2v5adj3",
"name": "Büro",
"kind": "ips",
"entries": [
"203.0.113.7",
"198.51.100.0/24"
],
"prefixCount": null,
"refreshedAt": null
},
{
"id": "mm6lznfy4ok1",
"name": "Telekom",
"kind": "asn",
"entries": [
"3320"
],
"prefixCount": 950,
"refreshedAt": "2026-10-30T15:42:00.000Z"
}
]
}POST/api/v1/firewalls/lists
Eine IP- oder AS-Liste; eine AS-Liste wird sofort bei RIPEstat aufgelöst und täglich aktualisiert. In einer Regel als peer {"kind":"list","id":…}.
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| namePflicht | Body | string | Name |
| kindPflicht | Body | string | ips oder asn |
| entriesPflicht | Body | array | Adressen und Netze, oder AS-Nummern wie AS3320 |
curl -X POST https://api.goodflare.io/api/v1/firewalls/lists \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Büro","kind":"ips","entries":["203.0.113.7","198.51.100.0/24"]}'{
"id": "k3vcv2v5adj3",
"name": "Büro",
"kind": "ips",
"entries": [
"203.0.113.7",
"198.51.100.0/24"
],
"prefixCount": null,
"refreshedAt": null
}PUT/api/v1/firewalls/lists/{listId}
Name und Einträge neu; Server, deren Firewall die Liste nutzt, werden binnen zehn Minuten neu geschrieben.
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| listIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| namePflicht | Body | string | Name |
| entriesPflicht | Body | array | die Einträge |
curl -X PUT https://api.goodflare.io/api/v1/firewalls/lists/LIST_ID \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Büro","entries":["203.0.113.7"]}'{
"id": "k3vcv2v5adj3",
"name": "Büro",
"kind": "ips",
"entries": [
"203.0.113.7"
],
"prefixCount": null,
"refreshedAt": null
}DELETE/api/v1/firewalls/lists/{listId}
Nur, wenn keine Regel sie nutzt (sonst 409).
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| listIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl -X DELETE https://api.goodflare.io/api/v1/firewalls/lists/LIST_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"ok": true
}GET/api/v1/servers/{id}/firewall
Die Firewall-Reihe des VPS und wie er sie gerade hat, direkt nachgelesen: own ist seine eigene Firewall (Richtlinien und ihre Regeln, ändern mit PUT /firewalls/{own.id}), shared die gemeinsamen Firewalls danach in ihrer Reihenfolge; die erste passende Regel entscheidet. notes nennt Regeln, die die Reihenfolge wirkungslos macht (shadow) oder die einer früheren widersprechen (conflict). state ist open, active, pending, applying, drift, failed oder notEnforced.
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/firewall \
-H "Authorization: Bearer $GF_TOKEN"{
"own": {
"id": "k7fq2m9xw0aa",
"name": "SMP mit Freunden",
"revision": 4
},
"shared": [
{
"id": "2q2bflmo9po6",
"name": "Webserver",
"ruleCount": 3
}
],
"ruleCount": 5,
"notes": [
{
"kind": "shadow",
"firewallId": "2q2bflmo9po6",
"ruleId": "5cbat01mng",
"byFirewallId": "k7fq2m9xw0aa",
"byFirewall": "SMP mit Freunden",
"byIndex": 0,
"byAction": "ACCEPT"
}
],
"state": "active",
"appliedAt": "2026-10-31T09:12:11.000Z",
"enforcement": {
"cluster": true,
"guest": true,
"nics": [
{
"name": "net0",
"firewall": true
}
]
},
"policyIn": "DROP",
"policyOut": "ACCEPT",
"drift": [],
"readAt": "2026-10-31T09:13:02.000Z"
}PUT/api/v1/servers/{id}/firewall
shared: die gemeinsamen Firewalls Ihres Kontos, die nach der eigenen gelten, in dieser Reihenfolge (leer: keine). Geschrieben wird im Hintergrund (202). Für ältere Skripte heißt {"firewallId": id} [id] und null [].
Berechtigung: Firewalls (firewall)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| sharedPflicht | Body | array | IDs gemeinsamer Firewalls, in Reihenfolge |
curl -X PUT https://api.goodflare.io/api/v1/servers/SERVER_ID/firewall \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"shared":["2q2bflmo9po6"]}'{
"shared": [
"2q2bflmo9po6"
],
"state": "pending"
}Endpunkte
GET/api/v1/networks
Die privaten Netzwerke Ihres Kontos mit Adressbereich (ipRange) und ihren Servern, je mit Adresse und Zustand (state: pending, applying, attached, detaching, failed; inGuest: das System des VPS hat die Adresse). available ist false, solange private Netzwerke noch nicht freigeschaltet sind. Nur mit einem Token für alle Server.
Berechtigung: Private Netzwerke (network)
curl https://api.goodflare.io/api/v1/networks \
-H "Authorization: Bearer $GF_TOKEN"{
"available": true,
"networks": [
{
"id": "f9qys6lsat3p",
"name": "Backend",
"ipRange": "10.20.0.0/24",
"trusted": true,
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"kind": "vm",
"ip": "10.20.0.2",
"state": "attached",
"inGuest": true
}
],
"createdAt": "2026-10-30T16:02:11.000Z"
}
],
"limits": {
"networks": 10,
"perServer": 3,
"members": 64,
"mtu": 1450
}
}POST/api/v1/networks
Ein neues Netzwerk, noch ohne Server. Der Bereich muss privat sein (RFC 1918), zwischen /16 und /29, und darf sich weder mit unseren internen Netzen noch mit Docker (172.17.0.0/16) noch mit Ihren anderen Netzwerken überschneiden (422 mit dem Grund); 10.20.0.5/24 wird als 10.20.0.0/24 genommen.
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| namePflicht | Body | string | Name, bis 60 Zeichen |
| ipRangePflicht | Body | string | Adressbereich in CIDR-Schreibweise, etwa 10.20.0.0/24 |
curl -X POST https://api.goodflare.io/api/v1/networks \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Backend","ipRange":"10.20.0.0/24"}'{
"id": "f9qys6lsat3p",
"name": "Backend",
"ipRange": "10.20.0.0/24",
"trusted": true,
"servers": [],
"createdAt": "2026-10-30T16:02:11.000Z"
}GET/api/v1/networks/{networkId}
Bereich, ob der Verkehr im Netz an den Firewalls vorbei darf (trusted), und die Server mit Adresse und Zustand.
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| networkIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl https://api.goodflare.io/api/v1/networks/NETWORK_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"id": "f9qys6lsat3p",
"name": "Backend",
"ipRange": "10.20.0.0/24",
"trusted": true,
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"kind": "vm",
"ip": "10.20.0.2",
"state": "attached",
"inGuest": true
}
],
"createdAt": "2026-10-30T16:02:11.000Z"
}PATCH/api/v1/networks/{networkId}
Umbenennen, oder trusted: true lässt allen Verkehr zwischen den Servern des Netzes vor den Firewall-Regeln durch (Standard), false lässt die Firewall des Servers auch dort entscheiden. Wird sofort auf die Server geschrieben.
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| networkIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| name | Body | string | optional |
| trusted | Body | boolean | optional |
curl -X PATCH https://api.goodflare.io/api/v1/networks/NETWORK_ID \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"trusted":false}'{
"id": "f9qys6lsat3p",
"name": "Backend",
"ipRange": "10.20.0.0/24",
"trusted": true,
"servers": [
{
"id": "k3Xb9QmZ2vLp8RtA",
"name": "SMP mit Freunden",
"kind": "vm",
"ip": "10.20.0.2",
"state": "attached",
"inGuest": true
}
],
"createdAt": "2026-10-30T16:02:11.000Z"
}DELETE/api/v1/networks/{networkId}
Nur, wenn kein Server mehr darin ist (sonst 409).
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| networkIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl -X DELETE https://api.goodflare.io/api/v1/networks/NETWORK_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"ok": true
}POST/api/v1/networks/{networkId}/servers
Ein VPS oder Container Ihres Kontos bekommt eine Netzwerkkarte im Netz und die Adresse ip, ohne Angabe die nächste freie. Geprüft wird sofort (409 oder 422 mit dem Grund, 503 solange private Netzwerke nicht verfügbar sind), eingerichtet im Hintergrund (202); wie weit, sagt GET /networks/{networkId}. Ein Server kann in bis zu drei Netzwerken sein.
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| networkIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| serverIdPflicht | Body | string | ID des Servers |
| ip | Body | string | optional, eine freie Adresse im Bereich |
curl -X POST https://api.goodflare.io/api/v1/networks/NETWORK_ID/servers \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"serverId":"bLDjcwHW3suHwnGG","ip":"10.20.0.10"}'{
"id": "k3Xb9QmZ2vLp8RtA",
"network": "f9qys6lsat3p",
"state": "pending"
}DELETE/api/v1/networks/{networkId}/servers/{serverId}
Die Netzwerkkarte des Servers im Netz wird entfernt, die Adresse wird frei; im Hintergrund (202).
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| networkIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
| serverIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl -X DELETE https://api.goodflare.io/api/v1/networks/NETWORK_ID/servers/SERVER_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"id": "k3Xb9QmZ2vLp8RtA",
"network": "f9qys6lsat3p",
"state": "detaching"
}GET/api/v1/servers/{id}/networks
In welchen Netzwerken der VPS ist, mit seiner Adresse und MAC dort und dem Zustand. Geht auch mit einem Token für bestimmte Server.
Berechtigung: Private Netzwerke (network)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/networks \
-H "Authorization: Bearer $GF_TOKEN"{
"available": true,
"networks": [
{
"id": "f9qys6lsat3p",
"name": "Backend",
"ipRange": "10.20.0.0/24",
"ip": "10.20.0.2",
"mac": "BC:24:11:46:89:6D",
"state": "attached",
"inGuest": true
}
]
}Endpunkte
GET/api/v1/servers/{id}/vpn
Zustand (state: ready, setup, unreachable), öffentliche Adresse, Port, öffentlicher Schlüssel des Servers, Netz des VPN, Werbeblocker, Verbrauch des Monats und Paket, die Geräte mit Zustand (connected, seen, never, paused) und letztem Handshake, die Weiterleitungen. Private Schlüssel gibt es hier nicht: die der Geräte kennen wir nicht.
Berechtigung: VPN (vpn)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
curl https://api.goodflare.io/api/v1/servers/SERVER_ID/vpn \
-H "Authorization: Bearer $GF_TOKEN"{
"state": "ready",
"publicIp": "5.230.123.140",
"port": 51820,
"alt443": false,
"mtu": 1320,
"serverPublicKey": "wSiOBJHaBrFX6+uU5HNRvLVN9KTvZRF8HRQ10oi3klc=",
"tunnel": {
"ipv4": "10.183.41.0/24",
"ipv6": "fd4e:1a2b:3c4d::/64",
"dns": "10.183.41.1"
},
"adblock": true,
"plan": {
"devices": 5,
"mbit": 50,
"trafficGb": 1000,
"forwards": 3,
"gateways": 1,
"team": false
},
"rateMbit": 50,
"month": {
"used": 182000000000,
"included": 1000000000000
},
"devices": [
{
"id": "OfdnezgAQz4k",
"name": "Laptop",
"kind": "computer",
"publicKey": "soDupA/0xbr1nwU+hs4OuFAVoRAHGsq4M6Ffn4h3my0=",
"addressV4": "10.183.41.2",
"addressV6": "fd4e:1a2b:3c4d::2",
"networks": [],
"tunnel": "all",
"paused": false,
"state": "connected",
"lastHandshake": "2026-11-01T09:14:03.000Z",
"monthBytes": 2100000000
}
],
"forwards": [
{
"id": "Fw2mXc5LzN1b",
"proto": "tcp",
"port": 443,
"deviceId": "OfdnezgAQz4k",
"target": "10.183.41.2:443",
"sources": []
}
]
}POST/api/v1/servers/{id}/vpn/devices
Ein Gerät mit Ihrem eigenen öffentlichen Schlüssel (wg genkey | wg pubkey); den privaten behalten Sie. Die Antwort nennt die Adressen im VPN; die Datei setzen Sie mit GET /servers/{id}/vpn selbst zusammen. Braucht das Recht Geräte verwalten.
Berechtigung: VPN (vpn)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| namePflicht | Body | string | bis 40 Zeichen |
| kindPflicht | Body | string | phone, computer, tablet oder router |
| publicKeyPflicht | Body | string | öffentlicher WireGuard-Schlüssel, 44 Zeichen |
| tunnel | Body | string | optional: all, noLocal, home oder custom |
| networks | Body | string | optional, bei kind router: Netze dahinter, etwa 192.168.178.0/24 |
curl -X POST https://api.goodflare.io/api/v1/servers/SERVER_ID/vpn/devices \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Laptop","kind":"computer","publicKey":"soDupA/0xbr1nwU+hs4OuFAVoRAHGsq4M6Ffn4h3my0="}'{
"id": "OfdnezgAQz4k",
"name": "Laptop",
"addressV4": "10.183.41.2",
"addressV6": "fd4e:1a2b:3c4d::2",
"networks": []
}DELETE/api/v1/servers/{id}/vpn/devices/{deviceId}
Das Gerät verliert sofort den Zugang; Weiterleitungen zu ihm werden mit entfernt. Braucht das Recht Geräte verwalten.
Berechtigung: VPN (vpn)
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
| idPflicht | Pfad | string | Die ID des Servers; steht in seiner Übersicht und in GET /servers. |
| deviceIdPflicht | Pfad | string | Die ID der Automation, aus GET …/automations. |
curl -X DELETE https://api.goodflare.io/api/v1/servers/SERVER_ID/vpn/devices/DEVICE_ID \
-H "Authorization: Bearer $GF_TOKEN"{
"ok": true
}Endpunkte
GET/api/v1/invoices
Rechnungen und Gutschriften Ihres Kontos, die neueste zuerst; Beträge in Cent. Ein Token für bestimmte Server sieht nur deren Rechnungen.
Berechtigung: Lesen (read)
curl https://api.goodflare.io/api/v1/invoices \
-H "Authorization: Bearer $GF_TOKEN"{
"invoices": [
{
"id": "Tq8vR2mXc5LzN1bW",
"number": "GF-2026-0042",
"kind": "renewal",
"status": "paid",
"totalCents": 1290,
"currency": "EUR",
"issuedAt": "2026-10-28T09:14:03.000Z",
"dueAt": "2026-11-11T09:14:03.000Z",
"paidAt": "2026-10-28T09:15:40.000Z"
}
]
}