TeamPanel
API & Entwickler

Medien-API

Dateien per API zu den Medien deines Teams hochladen, auflisten, taggen, in Ordnern organisieren und verwalten.

Medien-API

Mit der Medien-API laden Gameserver, Bots, Launcher, Handy-Skripte oder eigene Tools Dateien direkt zu den Medien deines Teams – und bekommen für öffentliche Dateien sofort einen CDN-Link zurück.

Grundlagen

WasWert
Basis-URLhttps://api.team-panel.com
Routenalle unter /v1/cdn/media/{teamId}
AuthentifizierungAuthorization: Bearer TP_… (Team-API-Schlüssel)
Rate Limit32 Anfragen / 10 Sekunden pro API-Schlüssel
Öffentliche URLshttps://cdn.team-panel.com/media/team/<teamId>/<datei>

Die Team-ID und die fertige Upload-URL findest du unter Medien im Tab API & Integrationen.

Eigener Schlüssel für Uploads

Lege für Gameserver und Skripte einen eigenen API-Schlüssel mit dem Scope media:upload an. Er kann dann nur die beiden Upload-Routen nutzen und lässt sich unabhängig rerollen oder löschen. Der Schlüssel hat zusätzlich nur die Rechte des verknüpften Mitglieds (bzw. des Owners) – für Uploads braucht es team.media.upload.

Schlüssel geheim halten

Lade Dateien möglichst serverseitig hoch. Ein Schlüssel im Client (z. B. im Spiel, in einer App oder im Browser) kann ausgelesen und missbraucht werden.


Endpunkte

Alle Pfade relativ zu /v1/cdn/media/{teamId}:

MethodePfadBeschreibungRecht
POST/upload/fileUpload als multipart/form-data (Gameserver, curl)team.media.upload
POST/uploadUpload als JSON mit Data-URI (serverseitige Skripte)team.media.upload
GET(Basis-Pfad)Dateien auflisten, filtern und suchenteam.media.view
GET/{mediaId}URL einer Datei (öffentlich oder signiert, 1 h gültig)team.media.view
PATCH/update/{mediaId}Dateiname, Sichtbarkeit, Ordner, Tags ändernteam.media.edit
DELETE/delete/{mediaId}Datei löschenteam.media.delete
POST/bulkMehrfachaktion für bis zu 100 Dateienedit / delete
GET/foldersOrdner auflistenteam.media.view
POST/foldersOrdner anlegenteam.media.edit
PATCH/folders/{folderId}Ordner umbenennen oder Regeln ändernteam.media.edit
DELETE/folders/{folderId}Ordner löschen (Inhalt und Unterordner wandern zum Parent)team.media.edit
GET/tags?search=Verwendete Tags mit Anzahlteam.media.view
GET/usage?days=30Speicher, Limits und tägliche Nutzung (1–90 Tage)team.media.view

Request- und Response-Schemas findest du in der Swagger UI.


Upload (multipart)

POST /v1/cdn/media/{teamId}/upload/file

Die Datei steht im Formularfeld file – sonst wird die erste Datei im Formular verwendet. Optionen als Query-Parameter:

ParameterBeschreibung
publictrue / false. Standard: true – bzw. die Standard-Sichtbarkeit des Zielordners
fileNameGespeicherter Dateiname. Standard: Name der hochgeladenen Datei
folderOrdnername oder Pfad, z. B. screenshots oder screenshots/2026. Fehlende Ordner werden angelegt
folderIdID eines bestehenden Ordners. Nicht zusammen mit folder
tagsKommagetrennt, z. B. license:abc123,type:screenshot

Antwort 201:

{
	"id": "4d6c…",
	"path": "media/team/<teamId>/<datei>.jpg",
	"publicUrl": "https://cdn.team-panel.com/media/team/<teamId>/<datei>.jpg",
	"usedTeamStorage": 1234567,
	"folderId": "8f1e…",
	"tags": ["license:abc123", "type:screenshot"]
}

Bei privaten Dateien ist publicUrl null – den signierten Link holst du dann über GET /{mediaId}.

curl -X POST "https://api.team-panel.com/v1/cdn/media/<teamId>/upload/file?folder=screenshots&tags=type:screenshot" \
  -H "Authorization: Bearer TP_dein_schluessel" \
  -F "file=@screenshot.jpg"
// Node.js 18+ (fetch, FormData und Blob sind eingebaut)
import { readFile } from 'node:fs/promises';

const TEAM_ID = process.env.TEAMPANEL_TEAM_ID;
const API_KEY = process.env.TEAMPANEL_API_KEY; // TP_…

const form = new FormData();
form.append(
	'file',
	new Blob([await readFile('screenshot.png')], { type: 'image/png' }),
	'screenshot.png',
);

const url = new URL(`https://api.team-panel.com/v1/cdn/media/${TEAM_ID}/upload/file`);
url.searchParams.set('folder', 'screenshots');
url.searchParams.set('tags', 'type:screenshot,discord:123456789');

const res = await fetch(url, {
	method: 'POST',
	headers: { Authorization: `Bearer ${API_KEY}` },
	body: form,
});
if (res.status !== 201)
	throw new Error(`Upload fehlgeschlagen (${res.status}): ${await res.text()}`);

const { id, publicUrl } = await res.json();
console.log(id, publicUrl);

Upload (JSON)

POST /v1/cdn/media/{teamId}/upload

Ideal für serverseitige Skripte, die die Datei bereits als Base64 bzw. Data-URI haben (z. B. FiveM mit screenshot-basic). Antwort wie beim Multipart-Upload.

{
	"file": "data:image/jpeg;base64,/9j/4AAQ…",
	"fileName": "screenshot.jpg",
	"public": true,
	"folder": "screenshots",
	"tags": ["license:abc123", "type:screenshot"]
}

public, folder, folderId und tags (hier als Array) sind optional – mit denselben Regeln wie oben.

Ohne public gilt die Standard-Sichtbarkeit des Zielordners. Ohne Ordnerregel sind JSON-Uploads privat (false), Multipart-Uploads dagegen öffentlich (true).

Komplettes Beispiel für FiveM: FiveM-Screenshots.


Dateien auflisten

GET /v1/cdn/media/{teamId}

QueryBeschreibung
folderIdOrdner-ID oder none (nur Dateien ohne Ordner)
includeSubfolderstrue – auch Dateien aus Unterordnern von folderId
tagsKommagetrennt; Datei muss alle Tags haben (UND)
searchSuche im Dateinamen
typeimage, video, audio, document oder all

Seitenweise Abfrage über die Header pageNumber (ab 1) und pageSize (Standard 50). Die Antwort enthält media, totalCount (Gesamtanzahl für die Filter) und usedTeamStorage.

# Alle Screenshots eines Spielers, Seite 2 mit je 48 Einträgen
curl "https://api.team-panel.com/v1/cdn/media/<teamId>?tags=license:abc123,type:screenshot" \
  -H "Authorization: Bearer TP_dein_schluessel" \
  -H "pageNumber: 2" \
  -H "pageSize: 48"

Datei abrufen, ändern, löschen

  • GET /{mediaId} → { "url": "…" } – bei öffentlichen Dateien der CDN-Link, bei privaten ein signierter Link (1 Stunde gültig)
  • PATCH /update/{mediaId} mit { "fileName"?, "public"?, "folderId"?, "tags"? } – folderId: null entfernt den Ordner, tags ersetzt alle Tags
  • DELETE /delete/{mediaId} – löscht die Datei und entfernt sie aus dem CDN-Cache

Mehrfachaktionen

POST /v1/cdn/media/{teamId}/bulk – für bis zu 100 Dateien:

actionZusätzliches FeldRecht
delete–team.media.delete
movefolderId (UUID oder null)team.media.edit
setPublicpublic (true / false)team.media.edit
addTagstags (Array)team.media.edit
removeTagstags (Array)team.media.edit
curl -X POST "https://api.team-panel.com/v1/cdn/media/<teamId>/bulk" \
  -H "Authorization: Bearer TP_dein_schluessel" \
  -H "Content-Type: application/json" \
  -d '{ "action": "move", "ids": ["<mediaId>", "<mediaId>"], "folderId": "<folderId>" }'
# → { "affected": 2, "failed": [] }

Dateien, die nicht verarbeitet werden konnten (z. B. fremde IDs oder mehr als 20 Tags), stehen in failed mit id und message.

Ordner

POST /folders legt einen Ordner an:

{
	"name": "screenshots",
	"parentId": null,
	"retentionDays": 30,
	"maxFiles": 500,
	"defaultPublic": true
}
FeldBeschreibung
name1–100 Zeichen, muss eindeutig sein (sonst 409)
parentIdÜbergeordneter Ordner für Unterordner (max. 5 Ebenen)
retentionDaysDateien nach N Tagen automatisch löschen
maxFilesNur die neuesten N Dateien behalten
defaultPublicStandard-Sichtbarkeit für Uploads in diesen Ordner

Die Regeln gelten nur für Dateien direkt im Ordner und werden stündlich angewendet. PATCH /folders/{folderId} akzeptiert dieselben Felder. Beim DELETE wandern Dateien und Unterordner in den übergeordneten Ordner.

Tags & Nutzung

  • GET /tags?search=license – meistgenutzte Tags inkl. Anzahl, z. B. für eine Autovervollständigung
  • GET /usage?days=30 – Speicher, Plan-Limits (null = unbegrenzt), Uploads pro Tag (inkl. API) und – falls aktiviert – CDN-Anfragen und -Traffic. Größen in Bytes.

Tag-Regeln: 1–64 Zeichen a-z 0-9 _ . : @ -, Kleinschreibung, max. 20 pro Datei. Empfohlen ist key:value, z. B. license:…, steam:…, discord:…, type:screenshot.


Fehlercodes

StatusBedeutung
400Ungültige Eingabe, ungültiger Tag, mehr als 20 Tags, ungültiger Ordner oder folder + folderId zusammen
401Ungültiger API-Schlüssel oder Schlüssel eines anderen Teams
402Plan-Limit erreicht: Datei zu groß (maxMediaFileSize) oder Team-Speicher voll (maxTeamStorage)
403Fehlendes Recht des Mitglieds oder fehlender Scope am Schlüssel
404Datei oder Ordner nicht gefunden
409Ordnername bereits vergeben
413Anfrage zu groß (Multipart 260 MB, JSON 140 MB – Dateien über ca. 100 MB nur per Multipart)
415Gesperrter Dateityp (HTML, JavaScript, ausführbare Dateien, Shell-Skripte, SVG)
429Rate Limit überschritten (32 Anfragen / 10 s pro Schlüssel)
503Upload-Kapazität vorübergehend belegt (error.media.uploadBusy); Retry-After: 5

Bei 503 mit error.media.uploadBusy warte mindestens die im Header Retry-After angegebene Zeit (aktuell 5 Sekunden). Sende danach denselben Upload erneut. Die API lehnt diese Anfrage vor der Verarbeitung des Anfrageinhalts ab; es wurde keine Datei angelegt. Dieser Fehler ist unabhängig vom Rate Limit (429).

Plan-Limits

Medien nutzen den gemeinsamen Team-Speicher (mit Mitglieder-Dateien, ToDo-Anhängen und Backups). Die max. Dateigröße steigt mit dem Plan (Free 10 MB bis Enterprise 250 MB). Aktuelle Werte liefert GET /usage (storage.usedBytes, limitBytes, mediaBytes, maxFileSizeBytes).

Automatisierung

Jeder Upload löst das Event Medien hochgeladen aus – z. B. um neue Screenshots in Discord zu posten. Siehe Automatisierung.

Siehe auch

Auf dieser Seite