Diese Referenz dokumentiert die nicht-administrative HTTP-API von FediSuite 2.0 (Repository FediSuite-Docker-Image, Routen unter server/routes).
Nicht enthalten:
- alle
/api/admin/...Endpunkte - der Catch-All
GET *, der nur die Web-App ausliefert
Wichtig:
- diese Datei beschreibt alle im Core-Repository statisch sichtbaren Endpunkte ohne Admin-Bereich
- zusätzlich können aktive Plugins eigene HTTP-Routen registrieren (siehe Abschnitt 11)
- diese Plugin-Routen lassen sich nicht aus dem Core-Repository ableiten, weil sie zur Laufzeit aus Plugin-Code gemountet werden
Grundregeln
Basis-URL
Alle Pfade sind relativ zu einer FediSuite-Instanz, zum Beispiel:
https://fedisuite.example.comhttps://app.fedisuite.com
Die Beispiele verwenden https://fedisuite.example.com.
Authentifizierung
Authentifizierte Requests verwenden:
Authorization: Bearer <JWT>
Das JWT wird über POST /api/auth/login bezogen. Hat das Konto eine Zwei-Faktor-Authentifizierung, liefert der Login zuerst eine Challenge und das JWT folgt erst nach POST /api/auth/2fa/verify (Abschnitt 3).
Eigenschaften des Tokens:
- das JWT ist mit HS256 signiert und trägt die Nutzer-ID und die ID einer Sitzung
- es hat kein Ablaufdatum, ist aber an eine Sitzung gebunden, die bei jedem Request geprüft wird
- eine Sitzung lässt sich über
DELETE /api/user/sessions/:sessionIdwiderrufen, danach antwortet die API mit401 - Challenge-Tokens aus dem Login mit 2FA sind keine Sitzungs-Tokens und werden an geschützten Endpunkten abgelehnt
Die Antworten der Authentifizierungs-Middleware haben keinen JSON-Body, sondern nur den Statuscode:
401kein Token, widerrufene Sitzung oder ein Token ohne Sitzungsdaten403Token mit ungültiger Signatur oder unlesbarem Format
Sprache
Fehlertexte werden über den Header Accept-Language lokalisiert. Unterstützt sind de, en, es, fr und it, alles andere fällt auf Englisch zurück.
Accept-Language: de
Zeitformat
Zeitstempel werden in der Regel als ISO-8601-Strings geliefert, meist in UTC.
Standard-Fehlerformat
Meistens:
{
"error": "Fehlermeldung"
}
Typische Statuscodes:
400ungültige Anfrage401fehlendes oder ungültiges Token (ohne JSON-Body)403Zugriff verweigert, zum Beispiel falsches Passwort404Ressource nicht gefunden oder Feature auf dieser Instanz abgeschaltet409Name bereits vergeben (Labels und Kampagnen)429Rate-Limit500interner Fehler
Antworten unter /api werden nicht gecacht (Cache-Control: no-store). CORS ist offen, die Authentifizierung läuft ausschließlich über den Bearer-Header.
Rate-Limits
Einige Endpunkte haben ein Limit pro IP-Adresse. Bei Überschreitung antwortet die API mit 429 und { "error": "..." }.
POST /api/auth/login: 10 Versuche in 15 MinutenPOST /api/auth/register,POST /api/mobile/auth/register,POST /api/auth/resend-verificationundPOST /api/mobile/auth/resend-verification: gemeinsam 5 Anfragen pro StundePOST /api/auth/forgot-password: 5 Anfragen in 15 MinutenPOST /api/auth/reset-password: 10 Anfragen in 15 MinutenPOST /api/auth/2fa/verify: 15 Versuche in 15 MinutenPOST /api/auth/2fa/email/request: 5 Anfragen in 15 Minuten- Verwaltung der Zwei-Faktor-Authentifizierung (
/api/auth/2fa/totp/...,/api/auth/2fa/recovery/regenerate,/api/auth/2fa/email/enable,/api/auth/2fa/email/disable): gemeinsam 20 Anfragen in 15 Minuten
Feature-Schalter
Betreiber*innen können größere Funktionen per Umgebungsvariable abschalten. Alle sind standardmäßig an. Die Routen eines abgeschalteten Features antworten mit 404, die aktuellen Schalter stehen unter features in GET /api/public/config.
ENABLE_DRAFTS: Entwürfe (features.drafts)ENABLE_CONTENT_CALENDAR: Content-Kalender (features.contentCalendar)ENABLE_CONTENT_LABELS: Labels und Kampagnen (features.contentLabels)ENABLE_CONTENT_RECYCLING: Wiederverwendung von Archiv-Beiträgen, Evergreen-Sammlung und Warnung vor ähnlichen Beiträgen (features.contentRecycling)ENABLE_CONTENT_INTELLIGENCE: Inhaltsanalyse (features.contentIntelligence)
Bei den Labels gilt eine Besonderheit: Ist das Feature aus, ignorieren Beitrags-Endpunkte die Felder labelIds und campaignId, Listen liefern labels: [] und campaign: null, und die Filter labelId und campaignId wirken nicht.
Scope-Regel
Fast alle Endpunkte sind an die Nutzer-ID aus dem Token gebunden. Nutzer*innen sehen also nur ihre eigenen Accounts, Beiträge, Entwürfe, Labels, Kampagnen, Dashboards und Einstellungen. Fremde IDs führen zu 404.
Übersicht aller dokumentierten Endpunkte
Public
GET /api/healthGET /api/public/configGET /api/public/noticeGET /api/mobile/public/infoGET /api/fedisuite/registry-challenge
Auth
POST /api/auth/registerPOST /api/mobile/auth/registerPOST /api/auth/resend-verificationPOST /api/mobile/auth/resend-verificationPOST /api/auth/verifyPOST /api/auth/loginGET /api/auth/providers/:providerId/startGET /api/auth/providers/:providerId/callbackPOST /api/auth/forgot-passwordPOST /api/auth/reset-passwordPOST /api/auth/fediverse/connectGET /api/auth/fediverse/callbackGET /api/auth/misskey/callbackPOST /api/auth/peertube/connect
Zwei-Faktor-Authentifizierung
POST /api/auth/2fa/email/requestPOST /api/auth/2fa/verifyGET /api/auth/2fa/statusPOST /api/auth/2fa/totp/setupPOST /api/auth/2fa/totp/confirmPOST /api/auth/2fa/totp/disablePOST /api/auth/2fa/recovery/regeneratePOST /api/auth/2fa/email/enablePOST /api/auth/2fa/email/disable
Plugin- und Provider-Discovery
GET /api/plugins/discoveryGET /api/plugins/:pluginId/web/manifestGET /api/plugins/:pluginId/web/assets/*GET /api/providers/discoveryPOST /api/providers/:providerId/connectGET /api/providers/:providerId/callbackPOST /api/providers/:providerId/callbackPOST /api/providers/:providerId/disconnectGET /api/plugin-settings/:pluginIdPUT /api/plugin-settings/:pluginId
Accounts und Notifications
GET /api/accountsGET /api/accounts/:id/notificationsPOST /api/accounts/:id/notifications/:notificationId/readPOST /api/accounts/:id/notifications/:notificationId/favouritePOST /api/accounts/:id/notifications/:notificationId/replyDELETE /api/accounts/:id
Beiträge, Entwürfe und Kalender
GET /api/postsGET /api/posts/:id/statusGET /api/posts/searchGET /api/posts/calendarPOST /api/postsPOST /api/posts/publish-nowPOST /api/posts/draftsPOST /api/posts/drafts/:idPOST /api/posts/delete-draftsGET /api/posts/:id/edit-sourcePOST /api/posts/:id/editPUT /api/posts/:idPOST /api/posts/:id/publishPOST /api/posts/:id/unschedulePOST /api/posts/:id/copy-to-draftPOST /api/posts/:id/repostDELETE /api/posts/:idPOST /api/posts/from-archivePUT /api/posts/:id/evergreenPOST /api/posts/similar
Labels und Kampagnen
GET /api/labelsPOST /api/labelsPUT /api/labels/:idDELETE /api/labels/:idGET /api/campaignsPOST /api/campaignsPUT /api/campaigns/:idDELETE /api/campaigns/:id
Account-Analytics und Refresh
POST /api/refresh-statsGET /api/accounts/:id/import-statusGET /api/accounts/:id/top-postsGET /api/accounts/:id/reach-summaryGET /api/accounts/:id/reach-postsGET /api/accounts/:id/posts/:postId/analysisGET /api/accounts/:id/daily-statsGET /api/accounts/:id/stats-historyGET /api/accounts/:id/engagement-rateGET /api/accounts/:id/weekly-growthGET /api/accounts/:id/engagement-breakdownGET /api/accounts/:id/best-timesGET /api/accounts/:id/best-times-quarterhourGET /api/accounts/:id/follower-eventsGET /api/accounts/:id/media-performanceGET /api/accounts/:id/weekday-engagementGET /api/accounts/:id/visibility-breakdownGET /api/accounts/:id/hashtag-overviewGET /api/accounts/:id/top-hashtagsGET /api/accounts/:id/hashtag-combinationsGET /api/accounts/:id/insightsGET /api/accounts/:id/content-insights
User Self-Service
GET /api/user/dashboard-layoutPUT /api/user/dashboard-layoutGET /api/user/dashboard-periodPUT /api/user/dashboard-periodGET /api/user/dashboard-selected-accountPUT /api/user/dashboard-selected-accountGET /api/user/posts-viewPUT /api/user/posts-viewGET /api/user/profilePUT /api/user/languagePUT /api/user/themePUT /api/user/emailPUT /api/user/passwordPUT /api/user/timezonePUT /api/user/default-accountGET /api/user/sessionsDELETE /api/user/sessions/:sessionIdGET /api/user/data-exportDELETE /api/user/account
Mobile Bundles
GET /api/mobile/bootstrapGET /api/mobile/accounts/:id/dashboardPUT /api/mobile/preferences
1. Public API
1.1 GET /api/health
Zweck:
- Erreichbarkeit prüfen
- Datenbankverbindung prüfen
- einfachste Kompatibilitätsprüfung für die Android-App
Auth:
- keine
Antwort bei Erfolg:
{
"status": "ok",
"db": "connected",
"timestamp": "2026-10-01T12:34:56.000Z"
}
Antwort bei DB-Problem (503):
{
"status": "error",
"db": "disconnected",
"error": "connect ECONNREFUSED ..."
}
curl:
curl -sS "https://fedisuite.example.com/api/health"
1.2 GET /api/public/config
Zweck:
- öffentliche Instanz-Konfiguration
- Zustand der Feature-Schalter
Auth:
- keine
Antwort:
{
"enableUserRegistration": true,
"appName": "FediSuite",
"publicSiteUrl": "https://fedisuite.example.com",
"authProviders": [],
"features": {
"drafts": true,
"contentCalendar": true,
"contentLabels": true,
"contentRecycling": true,
"contentIntelligence": true
}
}
curl:
curl -sS "https://fedisuite.example.com/api/public/config"
1.3 GET /api/public/notice
Zweck:
- globaler öffentlicher Hinweistext der Instanz
Auth:
- keine
Antwort:
{
"enabled": true,
"markdown": "Wartung heute ab 22:00 Uhr."
}
curl:
curl -sS "https://fedisuite.example.com/api/public/notice"
1.4 GET /api/mobile/public/info
Zweck:
- mobiles Public-Bundle für Login und Instanz-Auswahl
- kombiniert Config (inklusive
features), Notice und Auth-Fähigkeiten
Auth:
- keine
Antwort, gekürzt:
{
"enableUserRegistration": true,
"appName": "FediSuite",
"publicSiteUrl": "https://fedisuite.example.com",
"authProviders": [],
"features": {
"drafts": true,
"contentCalendar": true,
"contentLabels": true,
"contentRecycling": true,
"contentIntelligence": true
},
"notice": {
"enabled": false,
"markdown": ""
},
"connectorProviders": [],
"auth": {
"supportsLogin": true,
"supportsRegistration": true,
"supportsEmailVerification": true,
"supportsResendVerification": true,
"loginIdentifierMode": "email",
"pluginProviders": [],
"pluginAuthProviders": []
}
}
curl:
curl -sS "https://fedisuite.example.com/api/mobile/public/info"
1.5 GET /api/fedisuite/registry-challenge
Zweck:
- Eigentumsnachweis für das öffentliche FediSuite-Instanzverzeichnis
- wird von www.fedisuite.com aufgerufen, wenn Betreiber*innen ihre Instanz dort eintragen
Auth:
- keine
Query-Parameter:
tokender aktuell aktive Challenge-Token
Antwort:
{ "token": "..." }, aber nur wenntokenmit der aktiven, noch nicht abgelaufenen Challenge übereinstimmt- sonst
404
curl:
curl -sS "https://fedisuite.example.com/api/fedisuite/registry-challenge?token=abc123"
2. Auth API
2.1 POST /api/auth/register
Alias:
POST /api/mobile/auth/register
Zweck:
- neue Nutzer*in anlegen
Request-Body:
{
"email": "user@example.com",
"password": "geheimes-passwort",
"language": "de",
"timezone": "Europe/Berlin"
}
Regeln:
emailwird normalisiertpasswordmuss mindestens 8 Zeichen lang seinlanguageist optional und wird als Sprach-Tag normalisiert, ohne Angabe giltAccept-Languagetimezoneist optional- die Registrierung kann auf der Instanz abgeschaltet sein (
403, sieheenableUserRegistration) - die E-Mail-Adresse des Admins (
ADMIN_EMAIL) wird sofort als verifiziert angelegt - alle anderen Nutzer*innen erhalten einen Verifizierungslink per E-Mail
- bereits vergebene E-Mail-Adresse:
400
Antwort:
{
"success": true,
"requiresVerification": true,
"message": "Bitte bestätige zuerst deine E-Mail-Adresse.",
"user": {
"email": "user@example.com",
"language": "de",
"timezone": "Europe/Berlin"
}
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "geheimes-passwort",
"language": "de",
"timezone": "Europe/Berlin"
}'
Mobile-Alias:
curl -sS \
-X POST "https://fedisuite.example.com/api/mobile/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "geheimes-passwort"
}'
2.2 POST /api/auth/resend-verification
Alias:
POST /api/mobile/auth/resend-verification
Zweck:
- Verifizierungs-Mail erneut senden
Request-Body:
{
"email": "user@example.com"
}
oder:
{
"identifier": "user@example.com"
}
Besonderheiten:
- existiert die Nutzer*in nicht, kommt trotzdem eine neutrale Erfolgsantwort mit
requiresVerification: false - ist das Konto schon verifiziert, kommt Erfolg mit
alreadyVerified: true
Antworten:
{
"success": true,
"requiresVerification": true,
"message": "Bitte bestätige zuerst deine E-Mail-Adresse."
}
oder:
{
"success": true,
"requiresVerification": false,
"alreadyVerified": true,
"message": "Dein Konto wurde bestätigt."
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/resend-verification" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com"
}'
2.3 POST /api/auth/verify
Zweck:
- E-Mail-Verifikation abschließen
Request-Body:
{
"token": "hex-token-aus-mail"
}
Antwort:
{
"success": true,
"message": "Dein Konto wurde bestätigt."
}
Besonderheiten:
- der Token bleibt nach dem Erfolg gültig, ein zweiter Aufruf mit demselben Token ist also unschädlich und liefert wieder Erfolg
- unbekannter oder fehlender Token:
400
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/verify" \
-H "Content-Type: application/json" \
-d '{
"token": "hex-token-aus-mail"
}'
2.4 POST /api/auth/login
Zweck:
- Login mit E-Mail und Passwort
- liefert ein JWT oder, bei aktiver Zwei-Faktor-Authentifizierung, eine Challenge
Request-Body:
{
"identifier": "user@example.com",
"password": "mein-passwort"
}
Wichtig:
identifierist die E-Mail-Adresse, die Felderemailundusernamewerden als Alternativen füridentifiergelesen- unbekannte E-Mail-Adresse:
400 - unverifiziertes Konto oder falsches Passwort:
403 - fehlende Felder:
400
Antwort ohne 2FA:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"user": {
"id": 42,
"email": "user@example.com"
},
"isAdmin": false,
"auth": {
"type": "Bearer"
}
}
Antwort bei aktiver 2FA (noch kein JWT):
{
"requires_2fa": true,
"challenge_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"methods": ["totp", "email", "recovery"],
"expires_at": "2026-10-01T12:39:56.000Z"
}
Die Challenge ist fünf Minuten gültig. methods enthält die Verfahren, die das Konto nutzen kann: totp und recovery bei aktivem Authenticator, email bei aktivem E-Mail-Code. Weiter geht es mit POST /api/auth/2fa/verify (Abschnitt 3.2).
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/login" \
-H "Content-Type: application/json" \
-d '{
"identifier": "user@example.com",
"password": "mein-passwort"
}'
2.5 GET /api/auth/providers/:providerId/start
Zweck:
- Start eines Plugin-basierten Login-Providers
Auth:
- keine
Antwort:
- Redirect auf die Login-Seite des Providers, wenn das Plugin eine
redirect_urlliefert - sonst JSON mit weiteren Startdaten
- die genaue Semantik hängt vom jeweiligen Plugin ab
Fehler:
404, wenn der Provider keinen Start-Handler hat500bei einem Fehler im Plugin
Beispiel:
curl -i "https://fedisuite.example.com/api/auth/providers/demo-login/start"
2.6 GET /api/auth/providers/:providerId/callback
Zweck:
- Callback für Plugin-basierte Login-Provider
Auth:
- keine
Verhalten:
- das Plugin liefert eine Identität (
identityoderuser), FediSuite legt die Nutzer*in an oder findet sie - eine neue Sitzung wird angelegt und ein JWT erzeugt
- danach folgt ein Redirect auf die App mit
auth_tokenundis_adminim Query-String - eine Weiterleitung auf eine fremde Domain wird ignoriert, das Ziel ist immer die eigene Instanz
- liefert das Plugin keine Identität:
400
Wichtig:
- das ist ein typischer Browser-Flow, kein JSON-Endpunkt
Beispiel:
curl -i "https://fedisuite.example.com/api/auth/providers/demo-login/callback?code=abc&state=xyz"
2.7 POST /api/auth/forgot-password
Zweck:
- Passwort-Reset anfordern
Request-Body:
{
"email": "user@example.com"
}
Antwort:
{
"message": "Wenn ein Konto existiert, wurde eine E-Mail verschickt."
}
Besonderheiten:
- die Antwort unterscheidet absichtlich nicht zwischen existierender und nicht existierender E-Mail-Adresse
- der Reset-Token ist eine Stunde gültig
- fehlendes
email:400
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/forgot-password" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com"
}'
2.8 POST /api/auth/reset-password
Zweck:
- Passwort mit gültigem Reset-Token setzen
Request-Body:
{
"token": "reset-token",
"password": "neues-passwort"
}
Antwort:
{
"message": "Das Passwort wurde erfolgreich geändert."
}
Regeln:
- das neue Passwort muss mindestens 8 Zeichen lang sein
- der Token muss gültig und darf nicht abgelaufen sein (sonst
400)
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/reset-password" \
-H "Content-Type: application/json" \
-d '{
"token": "reset-token",
"password": "neues-passwort"
}'
2.9 POST /api/auth/fediverse/connect
Zweck:
- OAuth- oder MiAuth-Verbindungsaufbau für einen Fediverse-Account starten
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"instanceUrl": "https://mastodon.example"
}
instanceUrl darf auch ohne https:// angegeben werden. Der Server reduziert die URL auf den Origin und prüft sie gegen die Richtlinie für ausgehende Verbindungen (interne und nicht öffentliche Adressen werden abgelehnt).
Mögliche Antworten:
Normale OAuth-Instanz:
{
"redirectUrl": "https://mastodon.example/oauth/authorize?..."
}
Misskey-Familie:
{
"redirectUrl": "https://misskey.example/miauth/..."
}
PeerTube:
{
"requiresCredentials": true,
"instanceType": "peertube",
"instanceUrl": "https://video.example"
}
Der Server erkennt zuerst den Instanztyp. Unterstützt sind:
- Mastodon-kompatibel: Mastodon, Pleroma, Akkoma, GoToSocial, Pixelfed, Friendica, snac, Takahē, BizzFed, vutuv, Mitra und GNU social
- Misskey-Familie: Misskey, Sharkey, Iceshrimp, Calckey und Firefish (MiAuth)
- Vernissage
- Loops
- WordPress (über das ActivityPub-Plugin)
- PeerTube (Zugangsdaten statt Browser-Redirect, siehe 2.12)
- Plattformen, die Plugins als Provider ergänzen (siehe Abschnitt 4)
Fehler:
400, wenn die URL ungültig oder nicht erlaubt ist, der Instanztyp nicht erkannt wird oder die Plattform nicht unterstützt wird500, wenn die OAuth-Registrierung bei der Instanz scheitert
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/fediverse/connect" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instanceUrl": "https://mastodon.example"
}'
2.10 GET /api/auth/fediverse/callback
Zweck:
- OAuth-Callback für Mastodon-kompatible Instanzen, Loops, Vernissage und WordPress
Auth:
- keine, die Zuordnung zur Nutzer*in steckt im
state
Query-Parameter:
codestate
Verhalten:
- tauscht den OAuth-Code gegen ein Access-Token
- liest das Account-Profil
- legt einen Account an oder aktualisiert bei einem erneuten Verbinden (gleiche Instanz und gleicher Benutzername) den bestehenden Account, ohne Beiträge und Statistiken zu verlieren
- setzt
default_account_id, falls noch keiner existiert - startet bei einem neuen Account den historischen Import im Hintergrund
- leitet am Ende nach
/?tab=accountsweiter
Fehler (als Text, nicht als JSON):
400bei fehlendem oder ungültigemcodeoderstate502, wenn die Instanz hinter einem Bot-Schutz liegt oder kein Access-Token liefert500bei sonstigen Fehlern
Beispiel:
curl -i "https://fedisuite.example.com/api/auth/fediverse/callback?code=abc&state=xyz"
2.11 GET /api/auth/misskey/callback
Zweck:
- MiAuth-Callback für die Misskey-Familie
Auth:
- keine, die Zuordnung zur Nutzer*in steckt in der Session-ID
Query-Parameter:
session
Verhalten:
- validiert die Session bei der Remote-Instanz
- legt den verbundenen Account lokal an oder aktualisiert ihn
- startet den historischen Import
- leitet nach
/?tab=accountsweiter
Fehler (als Text): 400 bei fehlender oder ungültiger Session, 500 bei sonstigen Fehlern.
Beispiel:
curl -i "https://fedisuite.example.com/api/auth/misskey/callback?session=session-id"
2.12 POST /api/auth/peertube/connect
Zweck:
- PeerTube-Account per Benutzername und Passwort anbinden
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"instanceUrl": "https://video.example",
"username": "alice",
"password": "secret"
}
Antwort:
{
"success": true,
"reconnected": false,
"accountId": 123
}
Besonderheiten:
- kein Browser-Redirect
- der Server holt zuerst die lokalen OAuth-Client-Zugangsdaten der PeerTube-Instanz und meldet sich dann mit dem Passwort an
- danach wird ein Account angelegt (oder bei
reconnected: trueaktualisiert) und bei einem neuen Account ein Import gestartet - fehlende Felder oder eine ungültige oder nicht erlaubte URL:
400
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/peertube/connect" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instanceUrl": "https://video.example",
"username": "alice",
"password": "secret"
}'
3. Zwei-Faktor-Authentifizierung
Unterstützt sind drei Verfahren: ein Authenticator (TOTP), ein E-Mail-Code und Wiederherstellungscodes. Ist mindestens ein Faktor aktiv, verlangt der Login (POST /api/auth/login) einen zweiten Schritt. Die Endpunkte zur Verwaltung brauchen das aktuelle Passwort und, wenn TOTP aktiv ist, zusätzlich einen aktuellen TOTP-Code. Fehlerhafte Bestätigungen antworten mit 403.
3.1 POST /api/auth/2fa/email/request
Zweck:
- E-Mail-Code für den zweiten Login-Schritt anfordern
Auth:
- keine, stattdessen die Challenge aus dem Login
Request-Body:
{
"challenge_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."
}
Antwort:
{
"delivered": true,
"expires_at": "2026-10-01T12:44:56.000Z"
}
Regeln:
- der Code ist zehn Minuten gültig
- ein neuer Code kann frühestens nach einer Minute angefordert werden (sonst
429) - ungültige oder abgelaufene Challenge, oder E-Mail-Code nicht aktiviert:
400 - Versandfehler:
500
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/email/request" \
-H "Content-Type: application/json" \
-d '{
"challenge_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."
}'
3.2 POST /api/auth/2fa/verify
Zweck:
- zweiten Login-Schritt abschließen und das JWT erhalten
Auth:
- keine, stattdessen die Challenge aus dem Login
Request-Body:
{
"challenge_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"method": "totp",
"code": "123456"
}
method ist totp, email oder recovery. Bei recovery steht in code ein Wiederherstellungscode im Format XXXXX-XXXXX, der danach verbraucht ist.
Antwort bei Erfolg: dieselbe Antwort wie beim Login ohne 2FA.
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"user": {
"id": 42,
"email": "user@example.com"
},
"isAdmin": false,
"auth": {
"type": "Bearer"
}
}
Regeln:
- die Challenge wird nach einem Erfolg verbraucht
- falscher Code:
403 - ungültige oder abgelaufene Challenge, fehlender Code, nicht unterstützte Methode oder nicht aktivierter Faktor:
400 - E-Mail-Codes erlauben fünf Fehlversuche, danach ist der Code verbraucht und ein neuer muss angefordert werden
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/verify" \
-H "Content-Type: application/json" \
-d '{
"challenge_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
"method": "totp",
"code": "123456"
}'
3.3 GET /api/auth/2fa/status
Zweck:
- Stand der Zwei-Faktor-Authentifizierung des eigenen Kontos lesen
Auth:
- Bearer-Token erforderlich
Antwort:
{
"totp_enabled": true,
"totp_enabled_at": "2026-09-20T08:00:00.000Z",
"email_otp_enabled": false,
"recovery_codes_remaining": 10,
"setup_in_progress": false
}
curl:
curl -sS \
"https://fedisuite.example.com/api/auth/2fa/status" \
-H "Authorization: Bearer $TOKEN"
3.4 POST /api/auth/2fa/totp/setup
Zweck:
- Einrichtung des Authenticators starten
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"current_password": "mein-passwort"
}
Antwort:
{
"secret": "JBSWY3DPEHPK3PXP",
"otpauth_uri": "otpauth://totp/FediSuite:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=FediSuite",
"qr_data_url": "data:image/png;base64,..."
}
Regeln:
- das Geheimnis ist erst nach
POST /api/auth/2fa/totp/confirmaktiv - ist TOTP schon aktiv:
400 - falsches Passwort:
403
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/totp/setup" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"current_password": "mein-passwort"
}'
3.5 POST /api/auth/2fa/totp/confirm
Zweck:
- Einrichtung mit einem Code aus dem Authenticator abschließen
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"code": "123456"
}
Antwort:
{
"totp_enabled": true,
"recovery_codes": [
"ABCDE-FGHIJ",
"KLMNO-PQRST"
]
}
recovery_codes enthält zehn Codes und wird nur dieses eine Mal im Klartext geliefert. Frühere Codes werden dabei ungültig. Ohne vorheriges setup antwortet die API mit 400, bei falschem Code mit 403.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/totp/confirm" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "123456"
}'
3.6 POST /api/auth/2fa/totp/disable
Zweck:
- Authenticator deaktivieren
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"current_password": "mein-passwort",
"current_totp_code": "123456"
}
Antwort:
{
"totp_enabled": false
}
Die Wiederherstellungscodes werden dabei gelöscht. Ist TOTP nicht aktiv, antwortet die API mit 400.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/totp/disable" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"current_password": "mein-passwort",
"current_totp_code": "123456"
}'
3.7 POST /api/auth/2fa/recovery/regenerate
Zweck:
- neue Wiederherstellungscodes erzeugen, die alten werden ungültig
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"current_password": "mein-passwort",
"current_totp_code": "123456"
}
Antwort:
{
"recovery_codes": [
"ABCDE-FGHIJ",
"KLMNO-PQRST"
]
}
Voraussetzung ist ein aktiver Authenticator, sonst 400.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/recovery/regenerate" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"current_password": "mein-passwort",
"current_totp_code": "123456"
}'
3.8 POST /api/auth/2fa/email/enable
Zweck:
- E-Mail-Code als zweiten Faktor aktivieren
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"current_password": "mein-passwort",
"current_totp_code": "123456"
}
current_totp_code ist nur nötig, wenn TOTP schon aktiv ist.
Antwort:
{
"email_otp_enabled": true
}
Ist der E-Mail-Code schon aktiv, antwortet die API mit 400.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/email/enable" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"current_password": "mein-passwort"
}'
3.9 POST /api/auth/2fa/email/disable
Zweck:
- E-Mail-Code als zweiten Faktor deaktivieren
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"current_password": "mein-passwort",
"current_totp_code": "123456"
}
current_totp_code ist nur nötig, wenn TOTP aktiv ist.
Antwort:
{
"email_otp_enabled": false
}
Offene E-Mail-Codes werden dabei verworfen. Ist der E-Mail-Code nicht aktiv, antwortet die API mit 400.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/auth/2fa/email/disable" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"current_password": "mein-passwort"
}'
4. Plugin- und Provider-API
4.1 GET /api/plugins/discovery
Zweck:
- liefert den Discovery-Payload aller geladenen Plugins
Auth:
- Bearer-Token erforderlich
Antwort:
- Discovery-Payload der Plugin-Registry
- die Struktur hängt von den geladenen Plugins ab
curl:
curl -sS \
"https://fedisuite.example.com/api/plugins/discovery" \
-H "Authorization: Bearer $TOKEN"
4.2 GET /api/plugins/:pluginId/web/manifest
Zweck:
- liefert das öffentliche Web-Manifest eines aktiven Plugins
Auth:
- Bearer-Token erforderlich
Fehler:
404, wenn das Plugin nicht aktiv oder nicht gestartet ist oder kein Web-Manifest hat
curl:
curl -sS \
"https://fedisuite.example.com/api/plugins/fedisuite-plugin-bluesky/web/manifest" \
-H "Authorization: Bearer $TOKEN"
4.3 GET /api/plugins/:pluginId/web/assets/*
Zweck:
- liefert Web-Assets eines Plugins aus
Auth:
- keine, damit der Browser die Dateien als Skripte und Stylesheets laden kann
Verhalten:
- der Content-Type wird passend zur Datei gesetzt
Cache-Control: no-store404als Text, wenn Plugin oder Datei nicht gefunden werden,400bei einem ungültigen Pfad
curl:
curl -i "https://fedisuite.example.com/api/plugins/fedisuite-plugin-bluesky/web/assets/main.js"
4.4 GET /api/providers/discovery
Zweck:
- liefert alle von Plugins registrierten Connector-Provider
Auth:
- Bearer-Token erforderlich
Antwort:
{
"providers": []
}
curl:
curl -sS \
"https://fedisuite.example.com/api/providers/discovery" \
-H "Authorization: Bearer $TOKEN"
4.5 POST /api/providers/:providerId/connect
Zweck:
- startet den Verbindungsprozess für einen Plugin-Provider
Auth:
- Bearer-Token erforderlich
Antwort:
- plugin-spezifisch, oft eine Redirect-URL oder ein Start-Payload
404, wenn der Provider keinen Connect-Handler hat
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/providers/demo-provider/connect" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
4.6 GET /api/providers/:providerId/callback und POST /api/providers/:providerId/callback
Zweck:
- Callback für Plugin-Provider
Auth:
GET: keine, das ist der Browser-Rücksprung des ProvidersPOST: Bearer-Token erforderlich
Verhalten:
- führt den Provider-Callback aus
- liefert das Plugin kein
accountund keineuser_id, wird dessen Ergebnis unverändert als JSON zurückgegeben - sonst wird der Account gespeichert, bei
start_import !== falsestartet ein historischer Import - danach folgt ein Redirect (
redirect_urldes Plugins) oder die JSON-Antwort
Typische Erfolgsantwort:
{
"success": true,
"account_id": 123,
"provider_id": "demo-provider"
}
curl:
curl -i "https://fedisuite.example.com/api/providers/demo-provider/callback?code=abc&state=xyz"
oder authentifiziert:
curl -sS \
-X POST "https://fedisuite.example.com/api/providers/demo-provider/callback" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
4.7 POST /api/providers/:providerId/disconnect
Zweck:
- plugin-spezifisches Aufräumen beim Trennen
Auth:
- Bearer-Token erforderlich
Antwort:
- plugin-spezifisch
404, wenn der Provider keinen Disconnect-Handler hat
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/providers/demo-provider/disconnect" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
4.8 GET /api/plugin-settings/:pluginId
Zweck:
- plugin-spezifische Einstellungen lesen
Auth:
- Bearer-Token erforderlich
Query-Parameter:
scope=user|account|global, Standarduser- bei
scope=accountzusätzlichaccount_id
Wichtig:
scope=globalist nur für Admins erlaubt (sonst403)- die Berechtigungen werden gegen die Plugin-Permissions geprüft (sonst
403) 404, wenn das Plugin nicht aktiv ist oder der Account nicht der Nutzer*in gehört400, wenn das Plugin keine Einstellungen kennt, der Scope ungültig ist oder beiscope=accountdieaccount_idfehlt
Antwort:
{
"plugin_id": "demo-plugin",
"settings_schema": {},
"settings_values": {},
"settings_updated_at": "2026-10-01T12:00:00.000Z",
"scope": "user",
"scope_ref_id": "42"
}
curl:
curl -sS \
"https://fedisuite.example.com/api/plugin-settings/demo-plugin?scope=user" \
-H "Authorization: Bearer $TOKEN"
Account-spezifisch:
curl -sS \
"https://fedisuite.example.com/api/plugin-settings/demo-plugin?scope=account&account_id=123" \
-H "Authorization: Bearer $TOKEN"
4.9 PUT /api/plugin-settings/:pluginId
Zweck:
- plugin-spezifische Einstellungen schreiben
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"scope": "user",
"settings": {
"enabled": true
}
}
Bei scope=account:
{
"scope": "account",
"account_id": 123,
"settings": {
"enabled": true
}
}
Die Werte werden gegen das settingsSchema des Plugins geprüft, ungültige Werte führen zu 400. Wie beim Lesen gilt scope=global nur für Admins.
Antwort:
{
"plugin_id": "demo-plugin",
"settings_schema": {},
"settings_values": {
"enabled": true
},
"settings_updated_at": "2026-10-01T12:00:00.000Z",
"scope": "user",
"scope_ref_id": "42"
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/plugin-settings/demo-plugin" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scope": "user",
"settings": {
"enabled": true
}
}'
5. Accounts und Notifications
5.1 GET /api/accounts
Zweck:
- alle verbundenen Accounts der Nutzer*in
Auth:
- Bearer-Token erforderlich
Antwort, gekürzt:
[
{
"id": 123,
"instance_url": "https://mastodon.example",
"username": "alice",
"display_name": "Alice Example",
"avatar_url": "https://mastodon.example/media/avatar.jpg",
"stats_followers": 1200,
"stats_following": 300,
"stats_statuses": 800,
"max_characters": 500,
"characters_reserved_per_url": 23,
"max_media_attachments": 4,
"instance_type": "mastodon",
"composer_text_format": "plain",
"import_status": "done",
"auth_error_code": null,
"auth_error_message": null,
"auth_error_at": null,
"created_at": "2026-04-20T10:00:00.000Z",
"is_default": true,
"indexed_posts_count": 780,
"effective_statuses_count": 800,
"scheduled_posts_count": 2,
"failed_posts_count": 0
}
]
stats_followers kann null sein, wenn die Plattform den Wert verbirgt.
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts" \
-H "Authorization: Bearer $TOKEN"
5.2 GET /api/accounts/:id/notifications
Zweck:
- Notifications direkt von der angebundenen Zielplattform abrufen
Auth:
- Bearer-Token erforderlich
Query-Parameter:
limit, Standard30, Maximum80cursor, Pagination-Cursor ausnext_cursor
Unterstützte Plattformen:
- Mastodon-kompatibel (ohne GNU social)
- Misskey, Sharkey und Iceshrimp
- Vernissage
Für alle anderen Plattformen antwortet die API mit 400.
Antwort, generisch:
{
"items": [],
"next_cursor": null,
"support": {
"supported": true,
"can_reply": true,
"can_favourite": true,
"can_mark_read": true,
"reason": null
}
}
Besonderheiten:
- bei Account-Reauth-Fehlern kann
401mitcode: "account_reauth_required"kommen - fremde oder unbekannte Accounts:
404
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/notifications?limit=30" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept-Language: de"
5.3 POST /api/accounts/:id/notifications/:notificationId/read
Zweck:
- Notification als gelesen markieren
Auth:
- Bearer-Token erforderlich
Unterstützt:
- Mastodon-kompatible Plattformen, bei denen
can_mark_readwahr ist - Vernissage
Antwort:
{
"success": true
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/accounts/123/notifications/999/read" \
-H "Authorization: Bearer $TOKEN"
5.4 POST /api/accounts/:id/notifications/:notificationId/favourite
Zweck:
- auf den Beitrag zu einer Notification reagieren (favorisieren)
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"statusId": "114460000000000001"
}
Unterstützte Familien:
- Mastodon-kompatibel: Favourite
- Misskey-Familie: Reaction
- Vernissage: Favourite
Antwort:
{
"success": true
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/accounts/123/notifications/999/favourite" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"statusId": "114460000000000001"
}'
5.5 POST /api/accounts/:id/notifications/:notificationId/reply
Zweck:
- Antwort auf den Beitrag einer Notification senden
Auth:
- Bearer-Token erforderlich
Request-Body:
{
"statusId": "114460000000000001",
"content": "Danke!",
"visibility": "public"
}
Regeln:
statusIdist Pflicht undcontentdarf nicht leer seinvisibilityist optional, Standard istpublic- Misskey erlaubt hier kein
directoderspecified, die Sichtbarkeitenpublic,homeundfollowerssind möglich
Antwort:
{
"success": true,
"id": "114460000000000002"
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/accounts/123/notifications/999/reply" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"statusId": "114460000000000001",
"content": "Danke!",
"visibility": "public"
}'
5.6 DELETE /api/accounts/:id
Zweck:
- verbundenen Account entfernen
Auth:
- Bearer-Token erforderlich
Nebenwirkungen:
- löscht die lokalen Beiträge dieses Accounts samt Medien und Thumbnails
- Statistiken, Import-Jobs und Follower-Ereignisse des Accounts werden mitgelöscht
- setzt
default_account_idund den gewählten Dashboard-Account zurück, falls sie auf diesen Account zeigten - führt Plugin-Hooks und das Plugin-Disconnect-Cleanup aus
- Beiträge, die bereits auf der Plattform veröffentlicht sind, bleiben dort unverändert
Antwort:
{
"success": true
}
curl:
curl -sS \
-X DELETE "https://fedisuite.example.com/api/accounts/123" \
-H "Authorization: Bearer $TOKEN"
6. Beiträge, Entwürfe und Kalender
Ein Beitrag hat einen der Status draft, scheduled, processing, published und failed. Während der Veröffentlichung steht publish_phase nacheinander auf queued, uploading_media, processing_media (bei Videos, die die Plattform noch verarbeitet) und publishing, danach auf published oder failed. Einen Beitrag, der auf processing steht, fasst der Scheduler nicht noch einmal an. Bleibt er nach einem Absturz länger als 30 Minuten unverändert, wird er zurück auf scheduled gesetzt.
Beiträge und Entwürfe mit Medien werden als multipart/form-data gesendet. Hochgeladen werden können Bilder, Videos und PDF-Dateien (PDF nur auf Plattformen, die sie annehmen), jede Datei bis 50 MB und höchstens 20 Dateien pro Request. Der Server prüft den tatsächlichen Dateiinhalt, nicht nur den angegebenen Content-Type, und lehnt andere Dateien mit 400 ab. Die Zahl der Anhänge ist zusätzlich durch max_media_attachments des Accounts begrenzt, sonst 400.
Gemeinsame Formularfelder, soweit der Endpunkt sie liest:
accountId(bei Entwürfen alternativaccountIdsals JSON-Liste, siehe 6.7)content,spoilerText,title,visibility,languagevisibilityistpublic,unlisted,privateoderdirect, unbekannte Werte werden zupublic- mehrfach
media, dazu optionalaltText_0,altText_1, ... undfocusPoint_0,focusPoint_1, ... (Formatx,ymit Werten von -1 bis 1) pluginComposerDataals JSON-ObjektlabelIdsals JSON-Liste von Label-IDs (höchstens 20) undcampaignId, siehe Abschnitt 7
6.1 GET /api/posts
Zweck:
- Warteschlange, Entwürfe und Beitragshistorie der Nutzer*in lesen
Auth:
- Bearer-Token erforderlich
Query-Parameter:
page, Standard1pageSize, Standard20, Maximum100search, durchsucht Titel, Text, Benutzername und IDstatus=draft|scheduled|published|failed, ein Tab.scheduledenthält auchprocessing. Ohne Angabe oder bei einem anderen Wert kommen alle Beiträgesort=asc|desc, StandarddesclabelIdundcampaignId, nur Beiträge mit diesem Label oder dieser Kampagneevergreen=true, nur Entwürfe der Evergreen-Sammlung
Besonderheiten:
- im Tab
draftsteht ein Entwurf, der für mehrere Accounts gespeichert wurde, nur einmal in der Liste und trägtgroup_accountsmit den Accounts der Gruppe - jedes Element enthält
labelsundcampaignund die Account-Felderaccount_username,account_avatar,account_instance_url,account_instance_typeundaccount_composer_text_format
Antwort, gekürzt:
{
"items": [
{
"id": 991,
"account_id": 123,
"status": "scheduled",
"publish_phase": null,
"scheduled_at": "2026-10-07T08:00:00.000Z",
"title": null,
"content": "Geplanter Beitrag",
"spoiler_text": null,
"visibility": "public",
"language": "de",
"thread_group_id": null,
"crosspost_group_id": null,
"draft_stage": null,
"is_evergreen": false,
"account_username": "alice",
"labels": [{ "id": 4, "name": "Release", "color": "#5bc8f5" }],
"campaign": { "id": 2, "name": "Herbstkampagne" }
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total_items": 1,
"total_pages": 1,
"has_previous_page": false,
"has_next_page": false
},
"summary": {
"scheduled": 1,
"processing": 0,
"failed": 0,
"published": 12,
"draft": 3,
"total": 16
},
"filters": {
"search": "",
"status": "",
"sort": "desc",
"label_id": null,
"campaign_id": null
}
}
summary zählt alle Beiträge der Nutzer*in, unabhängig von Filtern. Entwürfe, die zu einer Gruppe gehören, zählen einmal.
curl:
curl -sS \
"https://fedisuite.example.com/api/posts?page=1&pageSize=20&status=scheduled" \
-H "Authorization: Bearer $TOKEN"
6.2 GET /api/posts/:id/status
Zweck:
- Status eines Beitrags abfragen, zum Beispiel um nach
publish-nowden Fortschritt zu verfolgen
Auth:
- Bearer-Token erforderlich
Antwort:
{
"id": 991,
"status": "processing",
"publish_phase": "uploading_media",
"fediverse_id": null,
"error_message": null
}
Fehler:
400bei einer ungültigen ID404, wenn der Beitrag nicht existiert oder jemand anderem gehört
curl:
curl -sS \
"https://fedisuite.example.com/api/posts/991/status" \
-H "Authorization: Bearer $TOKEN"
6.3 GET /api/posts/search
Zweck:
- Archivsuche über die bereits veröffentlichten und importierten Beiträge der Accounts
Auth:
- Bearer-Token erforderlich
Query-Parameter:
q, Suchtext (mehrere Wörter werden einzeln gesucht)page, Standard1, undpageSize, Standard20, Maximum100accountId, nur dieser Account (404, wenn er nicht der Nutzer*in gehört)includePrivate=true, durchsucht auch private und direkte Beiträge (ohne Angabe sind sie ausgeschlossen)sort=relevance|date|net_reach|gross_reach|engagement, Standardrelevancebei Suchtext, sonstdatesortDir=asc|desc, StandarddesclabelIdundcampaignId
Antwort, gekürzt:
{
"items": [
{
"fediverse_post_id": "114460000000000001",
"account_id": 123,
"content": "<p>Text des Beitrags</p>",
"url": "https://mastodon.example/@alice/114460000000000001",
"created_at": "2025-11-02T09:15:00.000Z",
"is_reply": false,
"total_engagement": 42,
"gross_reach": 5100,
"net_reach": 2300,
"reach_state": "complete",
"hashtags": ["fediverse"],
"account_username": "alice",
"recycling_hint": null
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total_items": 1,
"total_pages": 1,
"has_previous_page": false,
"has_next_page": false
},
"filters": {
"q": "fediverse",
"accountId": null,
"includePrivate": false,
"sort": "relevance",
"sortDir": "desc"
}
}
recycling_hint ist null oder ein Objekt mit age_days und reasons. Es markiert alte Beiträge, die deutlich mehr erreicht haben als der Median ihres Accounts (mindestens das 1,5-fache). Das Feld ist nur gefüllt, wenn ENABLE_CONTENT_RECYCLING an ist.
curl:
curl -sS \
"https://fedisuite.example.com/api/posts/search?q=fediverse&sort=net_reach" \
-H "Authorization: Bearer $TOKEN"
6.4 GET /api/posts/calendar
Zweck:
- Beiträge eines Zeitraums für den Content-Kalender lesen
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_CONTENT_CALENDAR, bei abgeschaltetem Feature404
Query-Parameter:
from(eingeschlossen) undto(ausgeschlossen), beide ISO-8601 und Pflicht. Der Zeitraum darf höchstens 45 Tage umfassen, sonst400accountId, nur dieser Accountplatform, nur Accounts dieser Plattform (zum Beispielmastodon)status, kommagetrennte Liste ausdraft,scheduled,processing,publishedundfailed. Standard ohnedraftlabelIdundcampaignId
Antwort, gekürzt:
{
"items": [
{
"id": 991,
"account_id": 123,
"status": "scheduled",
"scheduled_at": "2026-10-07T08:00:00.000Z",
"content": "Geplanter Beitrag",
"visibility": "public",
"thread_group_id": null,
"draft_stage": null,
"media_count": 1,
"media_previews": ["/uploads/thumbs/abc.jpg"],
"account_username": "alice",
"account_instance_type": "mastodon",
"labels": [],
"campaign": null
}
],
"truncated": false,
"limit": 500,
"range": {
"from": "2026-10-01T00:00:00.000Z",
"to": "2026-11-01T00:00:00.000Z"
}
}
Die Einträge sind nach scheduled_at aufsteigend sortiert. Es kommen höchstens 500 Beiträge, truncated zeigt an, dass mehr zutreffen.
curl:
curl -sS \
"https://fedisuite.example.com/api/posts/calendar?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&status=scheduled,published" \
-H "Authorization: Bearer $TOKEN"
6.5 POST /api/posts
Zweck:
- Beitrag einplanen
- optional mit Thread-Aufteilung und Medien
Auth:
- Bearer-Token erforderlich
Content-Type:
multipart/form-data
Form-Felder:
accountId(Pflicht),content,scheduledAt,spoilerText,visibility,title,languageautoSplitThread, teilt einen langen Text in einen Thread auf (nur auf Plattformen mit Thread-Unterstützung)media,altText_N,focusPoint_N,pluginComposerData,labelIds,campaignId(siehe Anfang von Abschnitt 6)
Regeln:
- ohne
scheduledAtgilt der aktuelle Zeitpunkt - PeerTube braucht einen
title languagewird validiert (400bei einem ungültigen Sprach-Tag)- vutuv unterstützt bestimmte Kombinationen aus Sichtbarkeit und Content-Warning nicht (
400) - die Teile eines Threads werden in einer Transaktion gespeichert
- unbekannter Account oder fremde Labels und Kampagnen:
404
Antwort:
- ein einzelner Beitrags-Datensatz oder
- ein Thread-Payload mit
thread_group_id,thread_totalunditems
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts" \
-H "Authorization: Bearer $TOKEN" \
-F "accountId=123" \
-F "content=Geplanter Beitrag" \
-F "scheduledAt=2026-10-07T08:00:00.000Z" \
-F "visibility=public" \
-F "language=de"
6.6 POST /api/posts/publish-now
Zweck:
- Beitrag sofort anlegen und direkt veröffentlichen
Auth:
- Bearer-Token erforderlich
Content-Type:
multipart/form-data
Form-Felder:
- wie bei
POST /api/posts, nur ohnescheduledAt
Verhalten:
- die Antwort kommt sofort, der Beitrag steht auf
processingmitpublish_phase: "queued" - die Veröffentlichung läuft im Hintergrund, den Verlauf zeigt
GET /api/posts/:id/status
Antwort:
- ein einzelner Beitrags-Datensatz oder
- ein Thread-Payload mit den Segmenten
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/publish-now" \
-H "Authorization: Bearer $TOKEN" \
-F "accountId=123" \
-F "content=Direkt raus" \
-F "visibility=public"
6.7 POST /api/posts/drafts
Zweck:
- neuen Entwurf speichern
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_DRAFTS
Content-Type:
multipart/form-data
Form-Felder:
accountIdsals JSON-Liste (zum Beispiel[123,124]) oderaccountIdfür einen Account. Es sind höchstens 20 Accounts möglich, für jeden entsteht ein Entwurf, in der Liste erscheinen sie als ein Eintragcontent,title,spoilerText,visibility,languagedraftStagemitidea,in_progressoderready, Standardin_progressmedia,altText_N,focusPoint_N,pluginComposerData,labelIds,campaignIdkeptMediaals JSON-Liste bereits gespeicherter Anhänge (token,alt,focal), vor allem für 6.8
Regeln:
- ein Entwurf hat keine Uhrzeit, der Scheduler veröffentlicht ihn nie
- ein Entwurf braucht einen Text oder mindestens einen Anhang (sonst
400) - ein unbekannter Account oder ein Account einer anderen Nutzer*in:
404
Antwort:
{
"primary_id": 1001,
"crosspost_group_id": "7f3c2b1a",
"items": [
{
"id": 1001,
"account_id": 123,
"status": "draft",
"draft_stage": "in_progress",
"content": "Idee für nächste Woche",
"scheduled_at": null,
"labels": [],
"campaign": null
}
]
}
primary_id ist der Entwurf, den die Oberfläche bearbeitet, crosspost_group_id verbindet die Entwürfe eines Eintrags.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/drafts" \
-H "Authorization: Bearer $TOKEN" \
-F "accountIds=[123,124]" \
-F "content=Idee für nächste Woche" \
-F "draftStage=idea"
6.8 POST /api/posts/drafts/:id
Zweck:
- Änderungen an einem Entwurf speichern
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_DRAFTS
Content-Type:
multipart/form-data
Form-Felder und Regeln wie bei 6.7. Die Account-Liste ersetzt die bisherige Auswahl der Gruppe, Accounts werden also ergänzt oder entfernt. Anhänge, die bleiben sollen, stehen in keptMedia, neue kommen als media. Labels und Kampagne bleiben unverändert, wenn die Felder fehlen.
Antwort:
- wie bei 6.7
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/drafts/1001" \
-H "Authorization: Bearer $TOKEN" \
-F "accountIds=[123]" \
-F "content=Überarbeitete Idee" \
-F "draftStage=ready"
6.9 POST /api/posts/delete-drafts
Zweck:
- mehrere Entwürfe mit einem Request löschen
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_DRAFTS
Request-Body:
{
"ids": [1001, 1002]
}
Regeln:
idsist eine Liste von einer bis 100 ganzen Zahlen größer als 0, sonst400- gelöscht werden nur Entwürfe der eigenen Nutzer*in, egal welche IDs der Request nennt
- ein Entwurf, der für mehrere Accounts gespeichert wurde, wird als Ganzes gelöscht
- Dateien der Entwürfe werden mitgelöscht
Antwort:
{
"deleted_ids": [1001, 1002]
}
deleted_ids enthält die IDs, die Entwürfe der Nutzer*in waren und jetzt weg sind.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/delete-drafts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": [1001, 1002]
}'
6.10 GET /api/posts/:id/edit-source
Zweck:
- alles lesen, was die Oberfläche zum Bearbeiten eines Beitrags braucht
Auth:
- Bearer-Token erforderlich
Antwort, gekürzt:
{
"id": 991,
"account_id": 123,
"status": "draft",
"draft_stage": "ready",
"crosspost_group_id": "7f3c2b1a",
"group_accounts": [
{ "post_id": 991, "account_id": 123 },
{ "post_id": 992, "account_id": 124 }
],
"is_evergreen": false,
"source": null,
"title": "",
"content": "Text des Entwurfs",
"spoiler_text": "",
"visibility": "public",
"language": "de",
"scheduled_at": null,
"labels": [],
"campaign": null,
"media": [
{
"token": "local:0",
"thumb_url": "/uploads/thumbs/abc.jpg",
"alt": "Alternativtext",
"focal": null,
"type": "image"
}
]
}
Besonderheiten:
group_accountsist nur bei Entwürfen gefülltsourcebeschreibt den Archiv-Beitrag, aus dem ein Entwurf entstanden ist (id,url,created_at), sonstnullmedia[].token(local:<index>oderremote:<index>) wird beim Speichern inkeptMediazurückgegeben. Bei veröffentlichten Beiträgen, deren Dateien nicht mehr vorliegen, liest der Server die Anhänge von der Plattform404, wenn der Beitrag nicht existiert oder jemand anderem gehört
curl:
curl -sS \
"https://fedisuite.example.com/api/posts/991/edit-source" \
-H "Authorization: Bearer $TOKEN"
6.11 POST /api/posts/:id/edit
Zweck:
- Beitrag vollständig bearbeiten (Text, Einstellungen, Anhänge, Labels) und neu einplanen oder sofort veröffentlichen
Auth:
- Bearer-Token erforderlich
Content-Type:
multipart/form-data
Form-Felder:
action,schedule(Standard) oderpublishscheduledAt, Pflicht beischeduleaccountId, optional, wenn der Beitrag auf einen anderen eigenen Account verschoben werden soll (ein fremder Account führt zu404)content,title,spoilerText,visibility,languagemedia,altText_N,focusPoint_N,keptMedia,pluginComposerData,labelIds,campaignIdautoSplitThread
Regeln:
- bearbeitbar sind Beiträge mit dem Status
scheduled,draft,failedundpublished, sonst400 - ein Entwurf wird durch das Einplanen oder Veröffentlichen zu einem normalen Beitrag
- ohne Text und ohne Anhang:
400 - schlägt die Bearbeitung fehl, werden hochgeladene Dateien wieder entfernt
Antwort:
- der gespeicherte Beitrag mit dem neuen Status, bei einem Thread mit
thread_group_id,thread_totalunditems - bei
action=publishläuft die Veröffentlichung im Hintergrund
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/991/edit" \
-H "Authorization: Bearer $TOKEN" \
-F "action=schedule" \
-F "scheduledAt=2026-10-08T10:00:00.000Z" \
-F "content=Überarbeiteter Beitrag" \
-F "visibility=public"
6.12 PUT /api/posts/:id
Zweck:
- einzelne Felder eines lokalen Beitrags ändern (leichte Bearbeitung, ohne Anhänge)
Auth:
- Bearer-Token erforderlich
Request-Body (JSON, alle Felder optional):
{
"content": "Aktualisiert",
"title": null,
"scheduledAt": "2026-10-08T10:00:00.000Z",
"spoilerText": "CW",
"visibility": "private",
"language": "de"
}
Regeln:
- bearbeitbar sind Beiträge mit dem Status
scheduled,draft,failedundpublished - leerer
contentist nur erlaubt, wenn der Beitrag Anhänge hat scheduledAtmuss ein gültiges Datum sein (400), bei bereits veröffentlichten Beiträgen wird die Zeit nicht geändert- PeerTube-Beiträge behalten einen Titel
scheduledAt,spoilerText,visibilityundlanguagegelten bei einem Thread für alle noch nicht veröffentlichten Segmente- ohne ein einziges bekanntes Feld:
400 - die Änderung betrifft den lokalen Datensatz, ein bereits auf der Plattform veröffentlichter Beitrag wird dadurch nicht geändert
Antwort:
- der aktualisierte Beitrags-Datensatz
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/posts/991" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Aktualisiert",
"visibility": "private",
"language": "de"
}'
6.13 POST /api/posts/:id/publish
Zweck:
- einen gespeicherten Beitrag sofort veröffentlichen oder einen fehlgeschlagenen erneut versuchen
Auth:
- Bearer-Token erforderlich
Regeln:
- möglich bei Status
scheduledundfailed, sonst400 - der Beitrag wird vorher auf
processinggesetzt, damit der Scheduler ihn nicht ein zweites Mal veröffentlicht - Teile eines Threads werden nicht unterstützt (
400) 404, wenn der Beitrag nicht existiert oder jemand anderem gehört
Antwort:
- der Beitrags-Datensatz mit Status
processing, die Veröffentlichung läuft im Hintergrund
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/991/publish" \
-H "Authorization: Bearer $TOKEN"
6.14 POST /api/posts/:id/unschedule
Zweck:
- die Planung zurücknehmen: aus einem eingeplanten oder fehlgeschlagenen Beitrag wird wieder ein Entwurf ohne Uhrzeit
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_DRAFTS
Regeln:
- nur bei Status
scheduledundfailedund nur, wenn der Beitrag kein Teil eines Threads ist, sonst400 - der Entwurf bekommt die Stufe
in_progress
Antwort:
- der Entwurf als Beitrags-Datensatz
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/991/unschedule" \
-H "Authorization: Bearer $TOKEN"
6.15 POST /api/posts/:id/copy-to-draft
Zweck:
- einen beliebigen Beitrag, auch einen veröffentlichten, in einen neuen Entwurf kopieren
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_DRAFTS
Verhalten:
- der Entwurf übernimmt Text, Content-Warning, Sichtbarkeit, Sprache, Labels und Kampagne
- Anhänge werden kopiert, Anhänge, die nur auf der Plattform liegen, werden heruntergeladen
- bei einem Entwurf, der für mehrere Accounts gespeichert wurde, entsteht eine Kopie für jeden Account
- während der Veröffentlichung (
processing) ist das Kopieren nicht möglich (400)
Antwort:
- wie bei 6.7
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/991/copy-to-draft" \
-H "Authorization: Bearer $TOKEN"
6.16 POST /api/posts/:id/repost
Zweck:
- bestehenden Beitrag erneut senden
Auth:
- Bearer-Token erforderlich
Verhalten:
- kopiert lokale Medien oder lädt, falls nötig, Remote-Medien nach
- erzeugt einen neuen lokalen Datensatz
- veröffentlicht sofort
Antwort:
{
"success": true,
"postId": 1009,
"fediverseId": "114460000000000099"
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/991/repost" \
-H "Authorization: Bearer $TOKEN"
6.17 DELETE /api/posts/:id
Zweck:
- lokalen Beitrag oder Teile eines Threads löschen
Auth:
- Bearer-Token erforderlich
Verhalten:
- ohne veröffentlichte Thread-Teile kann ein kompletter Thread gelöscht werden
- mit veröffentlichten Teilen nur die passenden verbleibenden Segmente
- ein Entwurf, der für mehrere Accounts gespeichert wurde, wird als Gruppe gelöscht
- Medien und Thumbnails werden aufgeräumt
- ein Beitrag, den es nicht gibt, ist kein Fehler
- bereits auf der Plattform veröffentlichte Beiträge bleiben dort bestehen
Antwort:
{
"success": true
}
curl:
curl -sS \
-X DELETE "https://fedisuite.example.com/api/posts/991" \
-H "Authorization: Bearer $TOKEN"
6.18 POST /api/posts/from-archive
Zweck:
- aus einem Beitrag des Archivs einen neuen Entwurf machen (Wiederverwendung)
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_CONTENT_RECYCLINGundENABLE_DRAFTS
Request-Body:
{
"accountId": 123,
"fediversePostId": "114460000000000001"
}
Verhalten:
- der Entwurf übernimmt den Text und, soweit die Plattform sie noch liefert, Content-Warning, Sprache und Anhänge
skipped_medianennt die Zahl der Anhänge, die nicht übernommen werden konnten- der Entwurf merkt sich den Archiv-Beitrag als Quelle (
sourceinedit-source) - fehlende oder ungültige Angaben:
400
Antwort: wie bei 6.7, dazu skipped_media.
{
"primary_id": 1010,
"crosspost_group_id": null,
"items": [],
"skipped_media": 0
}
items enthält den neuen Entwurf (im Beispiel gekürzt).
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/from-archive" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": 123,
"fediversePostId": "114460000000000001"
}'
6.19 PUT /api/posts/:id/evergreen
Zweck:
- einen Entwurf zur Evergreen-Sammlung hinzufügen oder daraus entfernen
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_CONTENT_RECYCLINGundENABLE_DRAFTS
Request-Body:
{
"evergreen": true
}
evergreen muss ein Boolean sein (sonst 400). Es lassen sich nur Entwürfe markieren, sonst 400. Die Markierung gilt für alle Entwürfe der Gruppe. Veröffentlicht wird dadurch nichts.
Antwort:
{
"ids": [1001],
"evergreen": true
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/posts/1001/evergreen" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"evergreen": true
}'
6.20 POST /api/posts/similar
Zweck:
- prüfen, ob ein Text einem kürzlich veröffentlichten Beitrag ähnelt
Auth:
- Bearer-Token erforderlich
Feature-Schalter:
ENABLE_CONTENT_RECYCLING
Request-Body:
{
"text": "Der Text, der gleich veröffentlicht werden soll",
"excludePostId": 991
}
Regeln:
textmuss ein String sein (sonst400),excludePostIdist optional und nimmt einen Beitrag von FediSuite von der Prüfung aus, zum Beispiel den, der gerade bearbeitet wird- verglichen werden die Beiträge der letzten 60 Tage mit öffentlicher oder nicht gelisteter Sichtbarkeit, ohne Antworten
- Texte unter 40 Zeichen werden nie verglichen, ab einer Ähnlichkeit von 0,8 (Skala 0 bis 1) gilt ein Beitrag als ähnlich
Antwort:
{
"similar": {
"similarity": 0.86,
"published_at": "2026-09-20T09:00:00.000Z",
"url": "https://mastodon.example/@alice/114460000000000001",
"account_id": 123
}
}
similar ist null, wenn nichts Ähnliches gefunden wurde. url ist null, wenn FediSuite die Adresse nicht kennt.
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/posts/similar" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Der Text, der gleich veröffentlicht werden soll"
}'
7. Labels und Kampagnen
Ein Label ist ein internes Etikett mit Namen und optionaler Farbe. Es wird nie veröffentlicht und nie in einen Hashtag umgewandelt. Eine Kampagne hat einen Namen, eine Beschreibung, einen optionalen Zeitraum und Labels. Ein Beitrag kann mehrere Labels und eine Kampagne tragen (Felder labelIds und campaignId, siehe Abschnitt 6).
Für alle Endpunkte dieses Abschnitts gilt:
- Bearer-Token erforderlich
- Feature-Schalter
ENABLE_CONTENT_LABELS, bei abgeschaltetem Feature404 - Namen sind pro Nutzer*in eindeutig, ohne Unterscheidung von Groß- und Kleinschreibung. Ein doppelter Name führt zu
409 - Löschen entfernt Label oder Kampagne von den Beiträgen, die Beiträge bleiben unverändert
- eine unbekannte oder fremde ID führt zu
404
7.1 GET /api/labels
Zweck:
- alle Labels der Nutzer*in, nach Namen sortiert
Antwort:
{
"items": [
{
"id": 4,
"name": "Release",
"color": "#5bc8f5",
"archived": false,
"post_count": 7
}
]
}
curl:
curl -sS \
"https://fedisuite.example.com/api/labels" \
-H "Authorization: Bearer $TOKEN"
7.2 POST /api/labels
Zweck:
- Label anlegen
Request-Body:
{
"name": "Release",
"color": "#5bc8f5"
}
Regeln:
nameist Pflicht, höchstens 64 Zeichen und ohne Steuerzeichencolorist optional und hat das Format#rrggbb, ein leerer Wert bedeutet keine Farbe
Antwort: das neue Label (wie ein Eintrag aus 7.1, post_count ist 0).
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/labels" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Release",
"color": "#5bc8f5"
}'
7.3 PUT /api/labels/:id
Zweck:
- Label ändern, es wird nur geändert, was im Body steht
Request-Body:
{
"name": "Releases",
"color": null,
"archived": true
}
name, color und archived sind einzeln optional. Ohne eines davon antwortet die API mit 400.
Antwort: das geänderte Label.
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/labels/4" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"archived": true
}'
7.4 DELETE /api/labels/:id
Zweck:
- Label löschen
Antwort:
{
"success": true
}
curl:
curl -sS \
-X DELETE "https://fedisuite.example.com/api/labels/4" \
-H "Authorization: Bearer $TOKEN"
7.5 GET /api/campaigns
Zweck:
- alle Kampagnen der Nutzer*in, nach Namen sortiert
Antwort:
{
"items": [
{
"id": 2,
"name": "Herbstkampagne",
"description": "Beiträge zum Herbst-Release",
"start_date": "2026-10-01",
"end_date": "2026-10-31",
"archived": false,
"label_ids": [4],
"post_count": 5
}
]
}
curl:
curl -sS \
"https://fedisuite.example.com/api/campaigns" \
-H "Authorization: Bearer $TOKEN"
7.6 POST /api/campaigns
Zweck:
- Kampagne anlegen
Request-Body:
{
"name": "Herbstkampagne",
"description": "Beiträge zum Herbst-Release",
"startDate": "2026-10-01",
"endDate": "2026-10-31",
"labelIds": [4]
}
Regeln:
nameist Pflicht und hat höchstens 100 Zeichendescriptionist optional und hat höchstens 2000 ZeichenstartDateundendDatesind optional im FormatYYYY-MM-DD, das Ende darf nicht vor dem Start liegen (400)labelIdssind die Labels, die die Kampagne mitbringt. Fremde Labels führen zu404
Antwort: die neue Kampagne (wie ein Eintrag aus 7.5).
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/campaigns" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Herbstkampagne",
"startDate": "2026-10-01",
"endDate": "2026-10-31",
"labelIds": [4]
}'
7.7 PUT /api/campaigns/:id
Zweck:
- Kampagne ändern, es wird nur geändert, was im Body steht
Request-Body: dieselben Felder wie bei 7.6, dazu archived (Boolean). labelIds ersetzt die Labels der Kampagne vollständig. Ohne eine einzige Änderung antwortet die API mit 400.
Antwort: die geänderte Kampagne.
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/campaigns/2" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endDate": "2026-11-15"
}'
7.8 DELETE /api/campaigns/:id
Zweck:
- Kampagne löschen, ihre Beiträge bleiben ohne Kampagne bestehen
Antwort:
{
"success": true
}
curl:
curl -sS \
-X DELETE "https://fedisuite.example.com/api/campaigns/2" \
-H "Authorization: Bearer $TOKEN"
8. Refresh und Analytics
Alle Endpunkte dieses Abschnitts brauchen ein Bearer-Token. Gehört der Account in der URL nicht der Nutzer*in, antwortet die API mit 404.
Der Parameter days begrenzt den Zeitraum auf die letzten days Tage. Fehlt er oder ist er 0, gilt der gesamte Zeitraum, außer bei reach-summary, reach-posts und content-insights, wo eigene Standardwerte gelten (siehe dort).
8.1 POST /api/refresh-stats
Zweck:
- alle Accounts der Nutzer*in sofort neu aktualisieren
Verhalten:
- ruft pro Account plattformspezifische Profil- und Statistik-Endpunkte auf
- aktualisiert Follower, Following, Beitragszähler, Profilmetadaten und Plattformlimits
- schreibt bei Änderungen (oder nach sechs Stunden) einen Eintrag in
stats_history - Accounts mit Reauth-Fehler werden übersprungen
- Fehler bei einzelnen Accounts brechen den Request nicht ab, sie werden nur protokolliert
Antwort:
{
"success": true
}
curl:
curl -sS \
-X POST "https://fedisuite.example.com/api/refresh-stats" \
-H "Authorization: Bearer $TOKEN"
8.2 GET /api/accounts/:id/import-status
Zweck:
- aktuellen Importstatus und letzten Importjob lesen
Antwort:
{
"import_status": "done",
"job": {
"status": "completed",
"phase": "posts",
"total_posts_fetched": 500,
"total_pages_fetched": 12,
"total_follow_events_fetched": 0,
"started_at": "2026-10-01T10:00:00.000Z",
"completed_at": "2026-10-01T10:04:00.000Z",
"error_message": null,
"next_retry_at": null,
"retry_count": 0
}
}
job ist null, wenn es noch keinen Importjob gibt.
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/import-status" \
-H "Authorization: Bearer $TOKEN"
8.3 GET /api/accounts/:id/top-posts
Zweck:
- die erfolgreichsten Beiträge eines Accounts
Query-Parameter:
dayslimit, Standard10, Maximum100sort=favourites_count|reblogs_count|replies_count|total_engagement|net_reach|gross_reach, Standardfavourites_count
Antwort:
- Array von Beiträgen aus dem Archiv mit
fediverse_post_id,content,url,created_at,favourites_count,reblogs_count,replies_count,visibility,media_count,total_engagement,net_reach,gross_reachundreach_state - leere Beiträge (ohne Text und ohne Medien) werden nicht aufgeführt
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/top-posts?days=30&limit=10&sort=total_engagement" \
-H "Authorization: Bearer $TOKEN"
8.4 GET /api/accounts/:id/reach-summary
Zweck:
- geschätzte Reichweite eines Accounts für einen Zeitraum, mit Vergleich zum Zeitraum davor und Tagesverlauf
Query-Parameter:
days, Standard30, Maximum730,0bedeutet gesamter Zeitraum
Antwort, gekürzt:
{
"days": 30,
"current": {
"posts_count": 18,
"analyzed_posts": 18,
"favourites": 320,
"boosts": 74,
"replies": 41,
"partial_posts": 1,
"complete_posts": 17,
"last_reach_fetch_at": "2026-10-01T08:00:00.000Z",
"net_reach": 21500,
"gross_reach": 48200
},
"previous": {
"posts_count": 15
},
"daily": [
{
"date": "2026-09-30",
"interactions": 12,
"analyzed_posts": 3,
"net_reach": 900,
"gross_reach": 2100
}
],
"pending_jobs": 0,
"algorithm_version": "fediwings-heuristic-v2"
}
previous hat dieselben Felder wie current und ist null, wenn days den gesamten Zeitraum meint. pending_jobs zählt die noch offenen Reichweiten-Berechnungen des Accounts. Die Reichweite ist eine Schätzung, algorithm_version nennt die verwendete Formel.
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/reach-summary?days=30" \
-H "Authorization: Bearer $TOKEN"
8.5 GET /api/accounts/:id/reach-posts
Zweck:
- Beiträge mit ihren Reichweiten-Details, nach einem Kriterium sortiert
Query-Parameter:
days, Standard30, Maximum730,0bedeutet gesamter Zeitraumlimit, Standard20, Maximum100sort=net_reach|gross_reach|boosts|date|favourites_count|reblogs_count|replies_count, Standardnet_reach
Antwort:
- Array mit
fediverse_post_id,content,url,created_at, den Zählern,total_engagement,gross_reach,net_reach,author_followers,booster_followers,visible_boosters,unattributed_boosts,reach_state(pending,partialodercomplete),reach_errorundreach_fetched_at - nur Beiträge mit der Sichtbarkeit
publicoderunlisted
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/reach-posts?days=90&limit=20&sort=net_reach" \
-H "Authorization: Bearer $TOKEN"
8.6 GET /api/accounts/:id/posts/:postId/analysis
Zweck:
- einen einzelnen Beitrag mit dem Rest des Accounts vergleichen und zeigen, welche Merkmale mit mehr Interaktionen zusammenhängen
Query-Parameter:
daysfür den Vergleichszeitraum, ohne Angabe der gesamte Zeitraum
postId ist die fediverse_post_id des Beitrags.
Antwort, gekürzt:
{
"post": {
"id": "114460000000000001",
"text": "Text des Beitrags",
"url": "https://mastodon.example/@alice/114460000000000001",
"created_at": "2026-09-20T09:00:00.000Z",
"favourites": 20,
"boosts": 5,
"replies": 2,
"total_engagement": 27
},
"overall_avg": 14.5,
"comparison_sample": 120,
"vs_average_percent": 86,
"factors": [
{
"key": "media",
"with_avg": 18.2,
"without_avg": 9.1,
"lift_percent": 100,
"sample_with": 60,
"sample_without": 60
}
]
}
factors nennt die Merkmale des Beitrags, die die Interaktionen der Vergleichsbeiträge verändern, der stärkste Effekt zuerst. Mögliche key: media, link, reply, question, hashtags, short_opener, number_opener und long_post. Ist der Beitrag nicht vorhanden, antwortet die API mit 404.
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/posts/114460000000000001/analysis?days=180" \
-H "Authorization: Bearer $TOKEN"
8.7 GET /api/accounts/:id/daily-stats
Query-Parameter:
days
Antwort:
[
{
"date": "2026-10-01",
"posts_count": 3,
"total_favourites": 20,
"total_reblogs": 5,
"total_replies": 2
}
]
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/daily-stats?days=30" \
-H "Authorization: Bearer $TOKEN"
8.8 GET /api/accounts/:id/stats-history
Query-Parameter:
days
Antwort:
[
{
"followers": 1200,
"following": 300,
"statuses": 800,
"recorded_at": "2026-10-01T09:00:00.000Z"
}
]
followers ist null, wenn die Plattform die Zahl an diesem Tag verborgen hat.
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/stats-history?days=30" \
-H "Authorization: Bearer $TOKEN"
8.9 GET /api/accounts/:id/engagement-rate
Antwort:
- Array mit
date,posts_countundengagement_rate(Interaktionen pro Beitrag und Tag)
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/engagement-rate?days=30" \
-H "Authorization: Bearer $TOKEN"
8.10 GET /api/accounts/:id/weekly-growth
Antwort:
- Array mit
week,follower_changeundfollowers_end
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/weekly-growth?days=90" \
-H "Authorization: Bearer $TOKEN"
8.11 GET /api/accounts/:id/engagement-breakdown
Antwort:
{
"favourites": 320,
"boosts": 74,
"replies": 41
}
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/engagement-breakdown?days=30" \
-H "Authorization: Bearer $TOKEN"
8.12 GET /api/accounts/:id/best-times
Antwort:
- Array mit
day_of_week(0 bis 6, Sonntag ist 0),hour,avg_engagementundpost_count
Hinweise:
avg_engagementist hier der Median der Interaktionen, trotz des Namens- dieser Endpunkt rechnet die Uhrzeit in UTC, das Mobile-Bundle rechnet die Zeitfenster dagegen in der Zeitzone der Nutzer*in
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/best-times?days=30" \
-H "Authorization: Bearer $TOKEN"
8.13 GET /api/accounts/:id/best-times-quarterhour
Antwort:
- Array mit
slot(0 bis 95, ein Slot ist 15 Minuten ab 00:00 UTC, über alle Wochentage zusammengefasst),avg_engagement(Median der Interaktionen) undpost_count
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/best-times-quarterhour?days=90" \
-H "Authorization: Bearer $TOKEN"
8.14 GET /api/accounts/:id/follower-events
Antwort:
- Array mit
date,followers_endundnet_change
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/follower-events?days=30" \
-H "Authorization: Bearer $TOKEN"
8.15 GET /api/accounts/:id/media-performance
Antwort:
- Array mit je einem Eintrag für
type: "media"undtype: "text"mitavg_engagement,post_count,avg_favourites,avg_boostsundavg_replies
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/media-performance?days=30" \
-H "Authorization: Bearer $TOKEN"
8.16 GET /api/accounts/:id/weekday-engagement
Antwort:
- Array mit
day_of_week(0 bis 6, Sonntag ist 0),avg_engagement,avg_favourites,avg_boosts,avg_repliesundpost_count
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/weekday-engagement?days=30" \
-H "Authorization: Bearer $TOKEN"
8.17 GET /api/accounts/:id/visibility-breakdown
Antwort:
- Array mit
visibilityundpost_count, die größte Gruppe zuerst
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/visibility-breakdown?days=30" \
-H "Authorization: Bearer $TOKEN"
8.18 GET /api/accounts/:id/hashtag-overview
Antwort:
{
"posts_total": 18,
"posts_with_hashtags": 9,
"posts_without_hashtags": 9,
"hashtag_uses": 22,
"unique_hashtags": 8,
"avg_hashtags_per_post": 1.22,
"avg_hashtags_per_tagged_post": 2.44
}
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/hashtag-overview?days=30" \
-H "Authorization: Bearer $TOKEN"
8.19 GET /api/accounts/:id/top-hashtags
Query-Parameter:
dayslimit, Standard12, Maximum30sort=total_engagement|avg_engagement|posts_count, Standardtotal_engagement
Antwort:
- Array mit
tag,posts_count,total_favourites,total_boosts,total_replies,total_engagement,avg_engagement,boost_rateundreply_rate
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/top-hashtags?days=30&limit=12&sort=avg_engagement" \
-H "Authorization: Bearer $TOKEN"
8.20 GET /api/accounts/:id/hashtag-combinations
Query-Parameter:
dayslimit, Standard10, Maximum25
Antwort:
- Array mit
tag_a,tag_b,posts_count,total_engagementundavg_engagement
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/hashtag-combinations?days=30&limit=10" \
-H "Authorization: Bearer $TOKEN"
8.21 GET /api/accounts/:id/insights
Zweck:
- serverseitig erzeugte Tipps zu Wachstum und Beiträgen
Query-Parameter:
days
Antwort:
- Objekt mit den Tipps, deren Struktur sich mit der Analyse-Engine ändern kann. Die Oberfläche und das Mobile-Bundle zeigen es an, ohne einzelne Felder vorauszusetzen
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/insights?days=30" \
-H "Authorization: Bearer $TOKEN"
8.22 GET /api/accounts/:id/content-insights
Zweck:
- Inhaltsanalyse eines Accounts: vergleicht Beiträge mit und ohne bestimmte Merkmale, findet auffällige Kombinationen, bewertet die Labels und nennt Zeitfenster der Woche mit deutlich besserer Reichweite
Feature-Schalter:
ENABLE_CONTENT_INTELLIGENCE, bei abgeschaltetem Feature404
Query-Parameter:
days, Standard180, Maximum730,0bedeutet gesamter Zeitraum
Regeln der Auswertung (zentral festgelegt, die Antwort nennt sie unter rules):
- eine Gruppe braucht mindestens 8 Beiträge, insgesamt müssen mindestens 20 Beiträge verglichen werden, sonst ist
has_enough_postsfalseunddimensions,patternsundtime_windowsbleiben leer - ein Unterschied zählt ab 15 Prozent und muss statistisch tragfähig sein (Welch-t-Wert ab 2, bei Kombinationen ab 3) und bestehen bleiben, wenn der stärkste Beitrag der besseren Gruppe entfällt
data_basisistlow,medium(ab 30 verglichenen Beiträgen) oderhigh(ab 100)- Sprache und Content-Warning sind nur für Beiträge bekannt, die FediSuite selbst gesendet hat
Antwort, gekürzt:
{
"days": 180,
"timezone": "Europe/Berlin",
"total_posts": 140,
"data_basis": "high",
"has_enough_posts": true,
"rules": {
"min_group_posts": 8,
"min_total_posts": 20,
"min_relative_difference": 0.15,
"min_welch_t": 2,
"min_welch_t_for_patterns": 3,
"medium_data_basis_from": 30,
"high_data_basis_from": 100,
"window_hours": 2,
"good_window_from": 0.4
},
"dimensions": [
{
"dimension": "media",
"known_posts": 140,
"total_posts": 140,
"data_basis": "high",
"groups": [
{
"key": "media",
"posts": 60,
"avg_net_reach": 410.5,
"avg_engagement": 18.2,
"avg_replies": 2.1,
"avg_boosts": 4.3,
"versus_rest": {
"net_reach": { "difference": 0.42, "is_significant": true, "reason": null },
"engagement": { "difference": 0.18, "is_significant": false, "reason": "trivial_effect" }
}
}
]
}
],
"patterns": [
{
"dimensions": [
{ "dimension": "media", "key": "media" },
{ "dimension": "visibility", "key": "public" }
],
"metric": "net_reach",
"difference": 0.55,
"posts": 30,
"other_posts": 110,
"average": 520.1,
"other_average": 335.4,
"data_basis": "high"
}
],
"labels": [
{
"label_id": 4,
"name": "Release",
"color": "#5bc8f5",
"key": "4",
"posts": 12,
"avg_net_reach": 380.0,
"data_basis": "medium",
"trend": { "difference": 0.2, "previous_posts": 9 }
}
],
"time_windows": [
{
"weekday": 2,
"start_hour": 18,
"end_hour": 20,
"level": "good",
"difference": 0.45,
"posts": 14,
"other_posts": 126,
"average": 560.0
}
]
}
Hinweise zu den Feldern:
dimensions[].dimensionistmedia,link,length,visibility,languageodercontent_warning, eine Dimension ohne ausreichende Datenbasis fehltversus_rest.<Maß>.reasonnennt die erste Regel, an der ein Unterschied gescheitert ist (too_few_posts,no_baseline,trivial_effect,not_significantoderoutlier_driven), bei bestandenen Unterschieden ist esnulldifferenceist relativ:0.42bedeutet 42 Prozent mehr als der Restlabels[].trendvergleicht mit dem Zeitraum davor und istnull, wenn einer der beiden Zeiträume zu wenige Beiträge hattime_windows[].weekdayzählt von 1 (Montag) bis 7 (Sonntag), die Stunden gelten in der Zeitzone der Nutzer*in.levelistgoododerabove_average
curl:
curl -sS \
"https://fedisuite.example.com/api/accounts/123/content-insights?days=180" \
-H "Authorization: Bearer $TOKEN"
9. User Self-Service
9.1 GET /api/user/dashboard-layout
Zweck:
- gespeichertes Dashboard-Layout lesen
Antwort:
- Array oder
null
curl:
curl -sS \
"https://fedisuite.example.com/api/user/dashboard-layout" \
-H "Authorization: Bearer $TOKEN"
9.2 PUT /api/user/dashboard-layout
Request-Body:
- ein Array als komplettes Layout, sonst
400
Antwort:
{
"success": true
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/dashboard-layout" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '["summary","top_posts","best_times"]'
9.3 GET /api/user/dashboard-period
Zweck:
- gespeicherten Statistik-Zeitraum des Dashboards lesen, geräteübergreifend
Antwort:
- eine Zahl aus
0,7,30,90,365,730(0ist der gesamte Zeitraum) odernull
curl:
curl -sS \
"https://fedisuite.example.com/api/user/dashboard-period" \
-H "Authorization: Bearer $TOKEN"
9.4 PUT /api/user/dashboard-period
Request-Body:
{
"period": 30
}
Erlaubt sind 0, 7, 30, 90, 365 und 730, sonst 400.
Antwort:
{
"success": true
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/dashboard-period" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"period": 30
}'
9.5 GET /api/user/dashboard-selected-account
Zweck:
- zuletzt im Dashboard gewählten Account lesen, geräteübergreifend und getrennt vom Standard-Account
Antwort:
- eine Account-ID oder
null
curl:
curl -sS \
"https://fedisuite.example.com/api/user/dashboard-selected-account" \
-H "Authorization: Bearer $TOKEN"
9.6 PUT /api/user/dashboard-selected-account
Request-Body:
{
"accountId": 123
}
Mit null oder ohne Wert wird die Auswahl zurückgesetzt. Ein Account, der nicht der Nutzer*in gehört, führt zu 404.
Antwort:
{
"success": true,
"dashboard_selected_account_id": 123
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/dashboard-selected-account" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": 123
}'
9.7 GET /api/user/posts-view
Zweck:
- gespeicherte Ansicht der Beitragsseite lesen
Antwort:
{
"view": "list"
}
view ist list (Standard) oder calendar.
curl:
curl -sS \
"https://fedisuite.example.com/api/user/posts-view" \
-H "Authorization: Bearer $TOKEN"
9.8 PUT /api/user/posts-view
Request-Body:
{
"view": "calendar"
}
Erlaubt sind list und calendar, sonst 400.
Antwort:
{
"view": "calendar"
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/posts-view" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"view": "calendar"
}'
9.9 GET /api/user/profile
Antwort:
{
"id": 42,
"email": "user@example.com",
"timezone": "Europe/Berlin",
"default_account_id": 123,
"language": "de",
"theme": "dark"
}
curl:
curl -sS \
"https://fedisuite.example.com/api/user/profile" \
-H "Authorization: Bearer $TOKEN"
9.10 PUT /api/user/language
Request-Body:
{
"language": "de"
}
language ist ein Sprach-Tag wie de, en oder pt-BR. Er wird normalisiert, ein Wert, der nicht wie ein Sprach-Tag aussieht, führt zu 400.
Antwort:
{
"success": true,
"language": "de"
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/language" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"language": "de"
}'
9.11 PUT /api/user/theme
Request-Body:
{
"theme": "dark"
}
Erlaubt sind dark und light, sonst 400.
Antwort:
{
"success": true,
"theme": "dark"
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/theme" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"theme": "dark"
}'
9.12 PUT /api/user/email
Request-Body:
{
"newEmail": "neu@example.com",
"currentPassword": "mein-passwort"
}
Antwort:
{
"success": true
}
Regeln:
- das aktuelle Passwort ist Pflicht (falsch:
403) - die neue E-Mail-Adresse darf nicht bereits von einem anderen Konto belegt sein (
400)
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/email" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"newEmail": "neu@example.com",
"currentPassword": "mein-passwort"
}'
9.13 PUT /api/user/password
Request-Body:
{
"currentPassword": "altes-passwort",
"newPassword": "neues-passwort"
}
Antwort:
{
"success": true
}
Regeln:
- beide Felder sind Pflicht
- das neue Passwort muss mindestens 8 Zeichen lang sein
- ein falsches aktuelles Passwort führt zu
403
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/password" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"currentPassword": "altes-passwort",
"newPassword": "neues-passwort"
}'
9.14 PUT /api/user/timezone
Request-Body:
{
"timezone": "Europe/Berlin"
}
timezone muss ein gültiger IANA-Zeitzonenname sein, sonst 400.
Antwort:
{
"success": true
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/timezone" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"timezone": "Europe/Berlin"
}'
9.15 PUT /api/user/default-account
Request-Body:
{
"accountId": 123
}
oder zum Zurücksetzen:
{
"accountId": null
}
Ein Account, der nicht der Nutzer*in gehört, führt zu 404.
Antwort:
{
"success": true,
"default_account_id": 123
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/user/default-account" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": 123
}'
9.16 GET /api/user/sessions
Zweck:
- aktive Sitzungen der Nutzer*in auflisten
Antwort:
{
"sessions": [
{
"id": "3f2a...64 Hex-Zeichen...c9",
"ip_address": "203.0.113.7",
"user_agent": "Mozilla/5.0 ...",
"created_at": "2026-09-30T08:00:00.000Z",
"last_seen_at": "2026-10-01T09:30:00.000Z"
}
],
"currentSessionId": "3f2a...64 Hex-Zeichen...c9"
}
Die Sitzungen sind nach last_seen_at absteigend sortiert. last_seen_at wird höchstens alle fünf Minuten aktualisiert.
curl:
curl -sS \
"https://fedisuite.example.com/api/user/sessions" \
-H "Authorization: Bearer $TOKEN"
9.17 DELETE /api/user/sessions/:sessionId
Zweck:
- eine Sitzung widerrufen, das zugehörige JWT ist danach ungültig
Regeln:
sessionIdist die ID aus 9.16 (64 Hexadezimalzeichen), sonst400- unbekannte oder bereits widerrufene Sitzung:
404
Antwort:
{
"success": true
}
curl:
curl -sS \
-X DELETE "https://fedisuite.example.com/api/user/sessions/$SESSION_ID" \
-H "Authorization: Bearer $TOKEN"
9.18 GET /api/user/data-export
Zweck:
- Datenexport der Nutzer*in (Auskunft nach Art. 15 DSGVO und Datenübertragbarkeit)
Antwort:
- ein ZIP-Archiv (
application/zip, Dateinamefedisuite-export-JJJJ-MM-TT.zip) - enthalten sind
auskunft.md,profil.md,konten.md,beitraege.md,verlauf.md,follower_ereignisse.md,statistiken.mdunddaten.jsonmit den Rohdaten - Passwörter und OAuth-Tokens sind nicht enthalten
- die Dokumente im Archiv sind deutschsprachig
curl:
curl -sS \
"https://fedisuite.example.com/api/user/data-export" \
-H "Authorization: Bearer $TOKEN" \
-o fedisuite-export.zip
9.19 DELETE /api/user/account
Zweck:
- die Nutzer*in samt Accounts und Beiträgen löschen
Request-Body:
{
"password": "mein-passwort"
}
Verhalten:
- Passwort-Prüfung (ohne Passwort
400, falsches Passwort403) - Medien-Cleanup
- Plugin-Cleanup für alle Accounts
- Löschung von Beiträgen, Accounts und Nutzer*in in einer Transaktion
Antwort:
{
"success": true
}
curl:
curl -sS \
-X DELETE "https://fedisuite.example.com/api/user/account" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"password": "mein-passwort"
}'
10. Mobile Bundle API
10.1 GET /api/mobile/bootstrap
Zweck:
- zentrales Mobile-Initialpayload
Auth:
- Bearer-Token erforderlich
Enthält:
server_timepublic_config, mitenableUserRegistration,appNameundpublicSiteUrl(die Feature-Schalter stehen inGET /api/public/configundGET /api/mobile/public/info)noticeuser, mitis_adminsummary, mit Zählern über alle Accounts (account_count,total_followers,scheduled_posts,failed_posts,published_posts,draft_posts,total_posts,importing_accounts,accounts_with_auth_errorsund weitere)accountsmobile_capabilities
Wichtige Capabilities:
dashboard_periods: aktuell[7, 30, 90, 365, 730, 0]top_post_sort_optionstop_hashtag_sort_optionssupports_preferences_batch_update: true
curl:
curl -sS \
"https://fedisuite.example.com/api/mobile/bootstrap" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept-Language: de"
10.2 GET /api/mobile/accounts/:id/dashboard
Zweck:
- komplettes Mobile-Dashboard-Bundle für einen Account
Auth:
- Bearer-Token erforderlich
Query-Parameter:
daystopPostsLimittopPostsSorttopHashtagsLimittopHashtagsSorthashtagCombinationsLimit
Defaults:
days=30,0bedeutet gesamter ZeitraumtopPostsLimit=5(1 bis 25)topHashtagsLimit=12(1 bis 25)hashtagCombinationsLimit=10(1 bis 25)topPostsSort=total_engagementtopHashtagsSort=total_engagement
Erlaubte Sorts:
- Top-Beiträge:
favourites_count,reblogs_count,replies_count,total_engagement - Top-Hashtags:
total_engagement,avg_engagement,posts_count
Bundle-Inhalt:
server_timeperiodaccount, mit den Zählernscheduled_posts_count,failed_posts_count,published_posts_count,draft_posts_countund den Feldern des letzten Imports (latest_import_*)summarychartstop_postsinsights
Besonderheiten:
best_timesundweekday_engagementwerden im Bundle in der Zeitzone der Nutzer*in berechneteffective_statuses_countbasiert aufGREATEST(stats_statuses, indexed_posts_count)- ein unbekannter Account führt zu
404, eine ungültige ID zu400
curl:
curl -sS \
"https://fedisuite.example.com/api/mobile/accounts/123/dashboard?days=30&topPostsSort=total_engagement&topHashtagsSort=avg_engagement" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept-Language: de"
10.3 PUT /api/mobile/preferences
Zweck:
- mehrere Nutzer-Einstellungen in einem Request aktualisieren
Auth:
- Bearer-Token erforderlich
Mögliche Felder:
languagethemetimezonedefaultAccountId
Ohne eines dieser Felder antwortet die API mit 400. Die Felder werden wie bei den Einzel-Endpunkten geprüft.
Antwort:
{
"success": true,
"profile": {
"id": 42,
"email": "user@example.com",
"timezone": "Europe/Berlin",
"default_account_id": 123,
"language": "de",
"theme": "dark"
}
}
curl:
curl -sS \
-X PUT "https://fedisuite.example.com/api/mobile/preferences" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"language": "de",
"theme": "dark",
"timezone": "Europe/Berlin",
"defaultAccountId": 123
}'
11. Dynamische Plugin-Routen
Neben den oben dokumentierten Endpunkten mountet FediSuite weitere Routen von Plugins unter /api/plugins/<pluginId>/<pfad>.
Wichtig:
- diese Routen sind nicht fest im Core-Repository verdrahtet
- sie hängen von installierten und aktivierten Plugins ab und werden aus der laufenden Registry aufgelöst
- jede Route legt fest, ob sie ohne Anmeldung, für angemeldete Nutzer*innen oder nur für Admins erreichbar ist
- ihre Dokumentation gehört in das jeweilige Plugin
Praktische Folge:
- diese Referenz ist für die Core-API vollständig
- für die vollständige Laufzeit-API einer konkreten Instanz kommen die Routen aller installierten Plugins hinzu
12. Praktischer Komplett-Workflow
Basis setzen:
BASE_URL="https://fedisuite.example.com"
Health prüfen:
curl -sS "$BASE_URL/api/health"
Login:
LOGIN_RESPONSE=$(curl -sS \
-X POST "$BASE_URL/api/auth/login" \
-H "Content-Type: application/json" \
-d '{
"identifier": "user@example.com",
"password": "mein-passwort"
}')
Token extrahieren (bei einem Konto ohne 2FA):
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.token')
Bei einem Konto mit 2FA enthält die Antwort statt token ein challenge_token. Dann folgt der zweite Schritt:
CHALLENGE=$(echo "$LOGIN_RESPONSE" | jq -r '.challenge_token')
TOKEN=$(curl -sS \
-X POST "$BASE_URL/api/auth/2fa/verify" \
-H "Content-Type: application/json" \
-d "{\"challenge_token\": \"$CHALLENGE\", \"method\": \"totp\", \"code\": \"123456\"}" \
| jq -r '.token')
Bootstrap laden:
curl -sS "$BASE_URL/api/mobile/bootstrap" \
-H "Authorization: Bearer $TOKEN"
Accounts laden:
curl -sS "$BASE_URL/api/accounts" \
-H "Authorization: Bearer $TOKEN"
Dashboard laden:
curl -sS "$BASE_URL/api/mobile/accounts/123/dashboard?days=30" \
-H "Authorization: Bearer $TOKEN"
Warteschlange laden:
curl -sS "$BASE_URL/api/posts?status=scheduled" \
-H "Authorization: Bearer $TOKEN"
Geplanten Beitrag anlegen:
curl -sS \
-X POST "$BASE_URL/api/posts" \
-H "Authorization: Bearer $TOKEN" \
-F "accountId=123" \
-F "content=Testbeitrag" \
-F "visibility=public" \
-F "scheduledAt=2026-10-07T08:00:00.000Z"
Entwurf speichern und später einplanen:
curl -sS \
-X POST "$BASE_URL/api/posts/drafts" \
-H "Authorization: Bearer $TOKEN" \
-F "accountId=123" \
-F "content=Idee für nächste Woche"
curl -sS \
-X POST "$BASE_URL/api/posts/1001/edit" \
-H "Authorization: Bearer $TOKEN" \
-F "action=schedule" \
-F "scheduledAt=2026-10-08T10:00:00.000Z" \
-F "content=Idee für nächste Woche"
Kalender eines Monats laden:
curl -sS \
"$BASE_URL/api/posts/calendar?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z" \
-H "Authorization: Bearer $TOKEN"