Ressourcen Core API Stand: FediSuite 2.0

FediSuite API

Diese Seite beschreibt die HTTP-API der FediSuite-Anwendung ohne den Admin-Bereich. Sie deckt öffentliche Endpunkte, Login mit Zwei-Faktor-Schritt und Verbindungsflüsse, Accounts, Beiträge, Entwürfe, den Kalender, Labels und Kampagnen, Analytics, Nutzer-Einstellungen und Mobile-Bundles ab, jeweils mit Request-Beispielen, JSON-Antworten und curl-Aufrufen.

Abdeckung

Alle Endpunkte des Core-Repositorys ohne /api/admin/....

Auth

JWT per Authorization: Bearer <JWT>, bezogen über POST /api/auth/login, bei aktiver 2FA nach POST /api/auth/2fa/verify.

Wichtige Grenze

Plugins können zur Laufzeit zusätzliche Routen mounten. Sie lassen sich nicht aus dem Core-Repository ableiten.

Wichtig: Diese Referenz ist für die Core-API vollständig, aber nicht automatisch die vollständige Runtime-API jeder beliebigen Instanz. Installierte Plugins können zusätzliche Endpunkte bereitstellen, die separat dokumentiert werden müssen.

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.com
  • https://app.fedisuite.com

Die Beispiele verwenden https://fedisuite.example.com.

Authentifizierung

Authentifizierte Requests verwenden:

http
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/:sessionId widerrufen, danach antwortet die API mit 401
  • 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:

  • 401 kein Token, widerrufene Sitzung oder ein Token ohne Sitzungsdaten
  • 403 Token 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.

http
Accept-Language: de

Zeitformat

Zeitstempel werden in der Regel als ISO-8601-Strings geliefert, meist in UTC.

Standard-Fehlerformat

Meistens:

json
{
  "error": "Fehlermeldung"
}

Typische Statuscodes:

  • 400 ungültige Anfrage
  • 401 fehlendes oder ungültiges Token (ohne JSON-Body)
  • 403 Zugriff verweigert, zum Beispiel falsches Passwort
  • 404 Ressource nicht gefunden oder Feature auf dieser Instanz abgeschaltet
  • 409 Name bereits vergeben (Labels und Kampagnen)
  • 429 Rate-Limit
  • 500 interner 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 Minuten
  • POST /api/auth/register, POST /api/mobile/auth/register, POST /api/auth/resend-verification und POST /api/mobile/auth/resend-verification: gemeinsam 5 Anfragen pro Stunde
  • POST /api/auth/forgot-password: 5 Anfragen in 15 Minuten
  • POST /api/auth/reset-password: 10 Anfragen in 15 Minuten
  • POST /api/auth/2fa/verify: 15 Versuche in 15 Minuten
  • POST /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/health
  • GET /api/public/config
  • GET /api/public/notice
  • GET /api/mobile/public/info
  • GET /api/fedisuite/registry-challenge

Auth

  • POST /api/auth/register
  • POST /api/mobile/auth/register
  • POST /api/auth/resend-verification
  • POST /api/mobile/auth/resend-verification
  • POST /api/auth/verify
  • POST /api/auth/login
  • GET /api/auth/providers/:providerId/start
  • GET /api/auth/providers/:providerId/callback
  • POST /api/auth/forgot-password
  • POST /api/auth/reset-password
  • POST /api/auth/fediverse/connect
  • GET /api/auth/fediverse/callback
  • GET /api/auth/misskey/callback
  • POST /api/auth/peertube/connect

Zwei-Faktor-Authentifizierung

  • POST /api/auth/2fa/email/request
  • POST /api/auth/2fa/verify
  • GET /api/auth/2fa/status
  • POST /api/auth/2fa/totp/setup
  • POST /api/auth/2fa/totp/confirm
  • POST /api/auth/2fa/totp/disable
  • POST /api/auth/2fa/recovery/regenerate
  • POST /api/auth/2fa/email/enable
  • POST /api/auth/2fa/email/disable

Plugin- und Provider-Discovery

  • GET /api/plugins/discovery
  • GET /api/plugins/:pluginId/web/manifest
  • GET /api/plugins/:pluginId/web/assets/*
  • GET /api/providers/discovery
  • POST /api/providers/:providerId/connect
  • GET /api/providers/:providerId/callback
  • POST /api/providers/:providerId/callback
  • POST /api/providers/:providerId/disconnect
  • GET /api/plugin-settings/:pluginId
  • PUT /api/plugin-settings/:pluginId

Accounts und Notifications

  • GET /api/accounts
  • GET /api/accounts/:id/notifications
  • POST /api/accounts/:id/notifications/:notificationId/read
  • POST /api/accounts/:id/notifications/:notificationId/favourite
  • POST /api/accounts/:id/notifications/:notificationId/reply
  • DELETE /api/accounts/:id

Beiträge, Entwürfe und Kalender

  • GET /api/posts
  • GET /api/posts/:id/status
  • GET /api/posts/search
  • GET /api/posts/calendar
  • POST /api/posts
  • POST /api/posts/publish-now
  • POST /api/posts/drafts
  • POST /api/posts/drafts/:id
  • POST /api/posts/delete-drafts
  • GET /api/posts/:id/edit-source
  • POST /api/posts/:id/edit
  • PUT /api/posts/:id
  • POST /api/posts/:id/publish
  • POST /api/posts/:id/unschedule
  • POST /api/posts/:id/copy-to-draft
  • POST /api/posts/:id/repost
  • DELETE /api/posts/:id
  • POST /api/posts/from-archive
  • PUT /api/posts/:id/evergreen
  • POST /api/posts/similar

Labels und Kampagnen

  • GET /api/labels
  • POST /api/labels
  • PUT /api/labels/:id
  • DELETE /api/labels/:id
  • GET /api/campaigns
  • POST /api/campaigns
  • PUT /api/campaigns/:id
  • DELETE /api/campaigns/:id

Account-Analytics und Refresh

  • POST /api/refresh-stats
  • GET /api/accounts/:id/import-status
  • GET /api/accounts/:id/top-posts
  • GET /api/accounts/:id/reach-summary
  • GET /api/accounts/:id/reach-posts
  • GET /api/accounts/:id/posts/:postId/analysis
  • GET /api/accounts/:id/daily-stats
  • GET /api/accounts/:id/stats-history
  • GET /api/accounts/:id/engagement-rate
  • GET /api/accounts/:id/weekly-growth
  • GET /api/accounts/:id/engagement-breakdown
  • GET /api/accounts/:id/best-times
  • GET /api/accounts/:id/best-times-quarterhour
  • GET /api/accounts/:id/follower-events
  • GET /api/accounts/:id/media-performance
  • GET /api/accounts/:id/weekday-engagement
  • GET /api/accounts/:id/visibility-breakdown
  • GET /api/accounts/:id/hashtag-overview
  • GET /api/accounts/:id/top-hashtags
  • GET /api/accounts/:id/hashtag-combinations
  • GET /api/accounts/:id/insights
  • GET /api/accounts/:id/content-insights

User Self-Service

  • GET /api/user/dashboard-layout
  • PUT /api/user/dashboard-layout
  • GET /api/user/dashboard-period
  • PUT /api/user/dashboard-period
  • GET /api/user/dashboard-selected-account
  • PUT /api/user/dashboard-selected-account
  • GET /api/user/posts-view
  • PUT /api/user/posts-view
  • GET /api/user/profile
  • PUT /api/user/language
  • PUT /api/user/theme
  • PUT /api/user/email
  • PUT /api/user/password
  • PUT /api/user/timezone
  • PUT /api/user/default-account
  • GET /api/user/sessions
  • DELETE /api/user/sessions/:sessionId
  • GET /api/user/data-export
  • DELETE /api/user/account

Mobile Bundles

  • GET /api/mobile/bootstrap
  • GET /api/mobile/accounts/:id/dashboard
  • PUT /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:

json
{
  "status": "ok",
  "db": "connected",
  "timestamp": "2026-10-01T12:34:56.000Z"
}

Antwort bei DB-Problem (503):

json
{
  "status": "error",
  "db": "disconnected",
  "error": "connect ECONNREFUSED ..."
}

curl:

bash
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:

json
{
  "enableUserRegistration": true,
  "appName": "FediSuite",
  "publicSiteUrl": "https://fedisuite.example.com",
  "authProviders": [],
  "features": {
    "drafts": true,
    "contentCalendar": true,
    "contentLabels": true,
    "contentRecycling": true,
    "contentIntelligence": true
  }
}

curl:

bash
curl -sS "https://fedisuite.example.com/api/public/config"

1.3 GET /api/public/notice

Zweck:

  • globaler öffentlicher Hinweistext der Instanz

Auth:

  • keine

Antwort:

json
{
  "enabled": true,
  "markdown": "Wartung heute ab 22:00 Uhr."
}

curl:

bash
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:

json
{
  "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:

bash
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:

  • token der aktuell aktive Challenge-Token

Antwort:

  • { "token": "..." }, aber nur wenn token mit der aktiven, noch nicht abgelaufenen Challenge übereinstimmt
  • sonst 404

curl:

bash
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:

json
{
  "email": "user@example.com",
  "password": "geheimes-passwort",
  "language": "de",
  "timezone": "Europe/Berlin"
}

Regeln:

  • email wird normalisiert
  • password muss mindestens 8 Zeichen lang sein
  • language ist optional und wird als Sprach-Tag normalisiert, ohne Angabe gilt Accept-Language
  • timezone ist optional
  • die Registrierung kann auf der Instanz abgeschaltet sein (403, siehe enableUserRegistration)
  • 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:

json
{
  "success": true,
  "requiresVerification": true,
  "message": "Bitte bestätige zuerst deine E-Mail-Adresse.",
  "user": {
    "email": "user@example.com",
    "language": "de",
    "timezone": "Europe/Berlin"
  }
}

curl:

bash
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:

bash
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:

json
{
  "email": "user@example.com"
}

oder:

json
{
  "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:

json
{
  "success": true,
  "requiresVerification": true,
  "message": "Bitte bestätige zuerst deine E-Mail-Adresse."
}

oder:

json
{
  "success": true,
  "requiresVerification": false,
  "alreadyVerified": true,
  "message": "Dein Konto wurde bestätigt."
}

curl:

bash
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:

json
{
  "token": "hex-token-aus-mail"
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "identifier": "user@example.com",
  "password": "mein-passwort"
}

Wichtig:

  • identifier ist die E-Mail-Adresse, die Felder email und username werden als Alternativen für identifier gelesen
  • unbekannte E-Mail-Adresse: 400
  • unverifiziertes Konto oder falsches Passwort: 403
  • fehlende Felder: 400

Antwort ohne 2FA:

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....",
  "user": {
    "id": 42,
    "email": "user@example.com"
  },
  "isAdmin": false,
  "auth": {
    "type": "Bearer"
  }
}

Antwort bei aktiver 2FA (noch kein JWT):

json
{
  "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:

bash
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_url liefert
  • sonst JSON mit weiteren Startdaten
  • die genaue Semantik hängt vom jeweiligen Plugin ab

Fehler:

  • 404, wenn der Provider keinen Start-Handler hat
  • 500 bei einem Fehler im Plugin

Beispiel:

bash
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 (identity oder user), 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_token und is_admin im 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:

bash
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:

json
{
  "email": "user@example.com"
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "token": "reset-token",
  "password": "neues-passwort"
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "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:

json
{
  "redirectUrl": "https://mastodon.example/oauth/authorize?..."
}

Misskey-Familie:

json
{
  "redirectUrl": "https://misskey.example/miauth/..."
}

PeerTube:

json
{
  "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 wird
  • 500, wenn die OAuth-Registrierung bei der Instanz scheitert

curl:

bash
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:

  • code
  • state

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=accounts weiter

Fehler (als Text, nicht als JSON):

  • 400 bei fehlendem oder ungültigem code oder state
  • 502, wenn die Instanz hinter einem Bot-Schutz liegt oder kein Access-Token liefert
  • 500 bei sonstigen Fehlern

Beispiel:

bash
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=accounts weiter

Fehler (als Text): 400 bei fehlender oder ungültiger Session, 500 bei sonstigen Fehlern.

Beispiel:

bash
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:

json
{
  "instanceUrl": "https://video.example",
  "username": "alice",
  "password": "secret"
}

Antwort:

json
{
  "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: true aktualisiert) und bei einem neuen Account ein Import gestartet
  • fehlende Felder oder eine ungültige oder nicht erlaubte URL: 400

curl:

bash
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:

json
{
  "challenge_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "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.

json
{
  "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:

bash
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:

json
{
  "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:

bash
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:

json
{
  "current_password": "mein-passwort"
}

Antwort:

json
{
  "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/confirm aktiv
  • ist TOTP schon aktiv: 400
  • falsches Passwort: 403

curl:

bash
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:

json
{
  "code": "123456"
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "current_password": "mein-passwort",
  "current_totp_code": "123456"
}

Antwort:

json
{
  "totp_enabled": false
}

Die Wiederherstellungscodes werden dabei gelöscht. Ist TOTP nicht aktiv, antwortet die API mit 400.

curl:

bash
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:

json
{
  "current_password": "mein-passwort",
  "current_totp_code": "123456"
}

Antwort:

json
{
  "recovery_codes": [
    "ABCDE-FGHIJ",
    "KLMNO-PQRST"
  ]
}

Voraussetzung ist ein aktiver Authenticator, sonst 400.

curl:

bash
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:

json
{
  "current_password": "mein-passwort",
  "current_totp_code": "123456"
}

current_totp_code ist nur nötig, wenn TOTP schon aktiv ist.

Antwort:

json
{
  "email_otp_enabled": true
}

Ist der E-Mail-Code schon aktiv, antwortet die API mit 400.

curl:

bash
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:

json
{
  "current_password": "mein-passwort",
  "current_totp_code": "123456"
}

current_totp_code ist nur nötig, wenn TOTP aktiv ist.

Antwort:

json
{
  "email_otp_enabled": false
}

Offene E-Mail-Codes werden dabei verworfen. Ist der E-Mail-Code nicht aktiv, antwortet die API mit 400.

curl:

bash
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:

bash
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:

bash
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-store
  • 404 als Text, wenn Plugin oder Datei nicht gefunden werden, 400 bei einem ungültigen Pfad

curl:

bash
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:

json
{
  "providers": []
}

curl:

bash
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:

bash
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 Providers
  • POST: Bearer-Token erforderlich

Verhalten:

  • führt den Provider-Callback aus
  • liefert das Plugin kein account und keine user_id, wird dessen Ergebnis unverändert als JSON zurückgegeben
  • sonst wird der Account gespeichert, bei start_import !== false startet ein historischer Import
  • danach folgt ein Redirect (redirect_url des Plugins) oder die JSON-Antwort

Typische Erfolgsantwort:

json
{
  "success": true,
  "account_id": 123,
  "provider_id": "demo-provider"
}

curl:

bash
curl -i "https://fedisuite.example.com/api/providers/demo-provider/callback?code=abc&state=xyz"

oder authentifiziert:

bash
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:

bash
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, Standard user
  • bei scope=account zusätzlich account_id

Wichtig:

  • scope=global ist nur für Admins erlaubt (sonst 403)
  • 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ört
  • 400, wenn das Plugin keine Einstellungen kennt, der Scope ungültig ist oder bei scope=account die account_id fehlt

Antwort:

json
{
  "plugin_id": "demo-plugin",
  "settings_schema": {},
  "settings_values": {},
  "settings_updated_at": "2026-10-01T12:00:00.000Z",
  "scope": "user",
  "scope_ref_id": "42"
}

curl:

bash
curl -sS \
  "https://fedisuite.example.com/api/plugin-settings/demo-plugin?scope=user" \
  -H "Authorization: Bearer $TOKEN"

Account-spezifisch:

bash
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:

json
{
  "scope": "user",
  "settings": {
    "enabled": true
  }
}

Bei scope=account:

json
{
  "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:

json
{
  "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:

bash
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:

json
[
  {
    "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:

bash
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, Standard 30, Maximum 80
  • cursor, Pagination-Cursor aus next_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:

json
{
  "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 401 mit code: "account_reauth_required" kommen
  • fremde oder unbekannte Accounts: 404

curl:

bash
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_read wahr ist
  • Vernissage

Antwort:

json
{
  "success": true
}

curl:

bash
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:

json
{
  "statusId": "114460000000000001"
}

Unterstützte Familien:

  • Mastodon-kompatibel: Favourite
  • Misskey-Familie: Reaction
  • Vernissage: Favourite

Antwort:

json
{
  "success": true
}

curl:

bash
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:

json
{
  "statusId": "114460000000000001",
  "content": "Danke!",
  "visibility": "public"
}

Regeln:

  • statusId ist Pflicht und content darf nicht leer sein
  • visibility ist optional, Standard ist public
  • Misskey erlaubt hier kein direct oder specified, die Sichtbarkeiten public, home und followers sind möglich

Antwort:

json
{
  "success": true,
  "id": "114460000000000002"
}

curl:

bash
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_id und 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:

json
{
  "success": true
}

curl:

bash
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 alternativ accountIds als JSON-Liste, siehe 6.7)
  • content, spoilerText, title, visibility, language
  • visibility ist public, unlisted, private oder direct, unbekannte Werte werden zu public
  • mehrfach media, dazu optional altText_0, altText_1, ... und focusPoint_0, focusPoint_1, ... (Format x,y mit Werten von -1 bis 1)
  • pluginComposerData als JSON-Objekt
  • labelIds als JSON-Liste von Label-IDs (höchstens 20) und campaignId, 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, Standard 1
  • pageSize, Standard 20, Maximum 100
  • search, durchsucht Titel, Text, Benutzername und ID
  • status=draft|scheduled|published|failed, ein Tab. scheduled enthält auch processing. Ohne Angabe oder bei einem anderen Wert kommen alle Beiträge
  • sort=asc|desc, Standard desc
  • labelId und campaignId, nur Beiträge mit diesem Label oder dieser Kampagne
  • evergreen=true, nur Entwürfe der Evergreen-Sammlung

Besonderheiten:

  • im Tab draft steht ein Entwurf, der für mehrere Accounts gespeichert wurde, nur einmal in der Liste und trägt group_accounts mit den Accounts der Gruppe
  • jedes Element enthält labels und campaign und die Account-Felder account_username, account_avatar, account_instance_url, account_instance_type und account_composer_text_format

Antwort, gekürzt:

json
{
  "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:

bash
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-now den Fortschritt zu verfolgen

Auth:

  • Bearer-Token erforderlich

Antwort:

json
{
  "id": 991,
  "status": "processing",
  "publish_phase": "uploading_media",
  "fediverse_id": null,
  "error_message": null
}

Fehler:

  • 400 bei einer ungültigen ID
  • 404, wenn der Beitrag nicht existiert oder jemand anderem gehört

curl:

bash
curl -sS \
  "https://fedisuite.example.com/api/posts/991/status" \
  -H "Authorization: Bearer $TOKEN"

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, Standard 1, und pageSize, Standard 20, Maximum 100
  • accountId, 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, Standard relevance bei Suchtext, sonst date
  • sortDir=asc|desc, Standard desc
  • labelId und campaignId

Antwort, gekürzt:

json
{
  "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:

bash
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 Feature 404

Query-Parameter:

  • from (eingeschlossen) und to (ausgeschlossen), beide ISO-8601 und Pflicht. Der Zeitraum darf höchstens 45 Tage umfassen, sonst 400
  • accountId, nur dieser Account
  • platform, nur Accounts dieser Plattform (zum Beispiel mastodon)
  • status, kommagetrennte Liste aus draft, scheduled, processing, published und failed. Standard ohne draft
  • labelId und campaignId

Antwort, gekürzt:

json
{
  "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:

bash
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, language
  • autoSplitThread, 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 scheduledAt gilt der aktuelle Zeitpunkt
  • PeerTube braucht einen title
  • language wird validiert (400 bei 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_total und items

curl:

bash
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 ohne scheduledAt

Verhalten:

  • die Antwort kommt sofort, der Beitrag steht auf processing mit publish_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:

bash
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:

  • accountIds als JSON-Liste (zum Beispiel [123,124]) oder accountId für einen Account. Es sind höchstens 20 Accounts möglich, für jeden entsteht ein Entwurf, in der Liste erscheinen sie als ein Eintrag
  • content, title, spoilerText, visibility, language
  • draftStage mit idea, in_progress oder ready, Standard in_progress
  • media, altText_N, focusPoint_N, pluginComposerData, labelIds, campaignId
  • keptMedia als 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:

json
{
  "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:

bash
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:

bash
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:

json
{
  "ids": [1001, 1002]
}

Regeln:

  • ids ist eine Liste von einer bis 100 ganzen Zahlen größer als 0, sonst 400
  • 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:

json
{
  "deleted_ids": [1001, 1002]
}

deleted_ids enthält die IDs, die Entwürfe der Nutzer*in waren und jetzt weg sind.

curl:

bash
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:

json
{
  "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_accounts ist nur bei Entwürfen gefüllt
  • source beschreibt den Archiv-Beitrag, aus dem ein Entwurf entstanden ist (id, url, created_at), sonst null
  • media[].token (local:<index> oder remote:<index>) wird beim Speichern in keptMedia zurückgegeben. Bei veröffentlichten Beiträgen, deren Dateien nicht mehr vorliegen, liest der Server die Anhänge von der Plattform
  • 404, wenn der Beitrag nicht existiert oder jemand anderem gehört

curl:

bash
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) oder publish
  • scheduledAt, Pflicht bei schedule
  • accountId, optional, wenn der Beitrag auf einen anderen eigenen Account verschoben werden soll (ein fremder Account führt zu 404)
  • content, title, spoilerText, visibility, language
  • media, altText_N, focusPoint_N, keptMedia, pluginComposerData, labelIds, campaignId
  • autoSplitThread

Regeln:

  • bearbeitbar sind Beiträge mit dem Status scheduled, draft, failed und published, sonst 400
  • 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_total und items
  • bei action=publish läuft die Veröffentlichung im Hintergrund

curl:

bash
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):

json
{
  "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, failed und published
  • leerer content ist nur erlaubt, wenn der Beitrag Anhänge hat
  • scheduledAt muss 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, visibility und language gelten 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:

bash
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 scheduled und failed, sonst 400
  • der Beitrag wird vorher auf processing gesetzt, 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:

bash
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 scheduled und failed und nur, wenn der Beitrag kein Teil eines Threads ist, sonst 400
  • der Entwurf bekommt die Stufe in_progress

Antwort:

  • der Entwurf als Beitrags-Datensatz

curl:

bash
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:

bash
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:

json
{
  "success": true,
  "postId": 1009,
  "fediverseId": "114460000000000099"
}

curl:

bash
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:

json
{
  "success": true
}

curl:

bash
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_RECYCLING und ENABLE_DRAFTS

Request-Body:

json
{
  "accountId": 123,
  "fediversePostId": "114460000000000001"
}

Verhalten:

  • der Entwurf übernimmt den Text und, soweit die Plattform sie noch liefert, Content-Warning, Sprache und Anhänge
  • skipped_media nennt die Zahl der Anhänge, die nicht übernommen werden konnten
  • der Entwurf merkt sich den Archiv-Beitrag als Quelle (source in edit-source)
  • fehlende oder ungültige Angaben: 400

Antwort: wie bei 6.7, dazu skipped_media.

json
{
  "primary_id": 1010,
  "crosspost_group_id": null,
  "items": [],
  "skipped_media": 0
}

items enthält den neuen Entwurf (im Beispiel gekürzt).

curl:

bash
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_RECYCLING und ENABLE_DRAFTS

Request-Body:

json
{
  "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:

json
{
  "ids": [1001],
  "evergreen": true
}

curl:

bash
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:

json
{
  "text": "Der Text, der gleich veröffentlicht werden soll",
  "excludePostId": 991
}

Regeln:

  • text muss ein String sein (sonst 400), excludePostId ist 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:

json
{
  "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:

bash
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 Feature 404
  • 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:

json
{
  "items": [
    {
      "id": 4,
      "name": "Release",
      "color": "#5bc8f5",
      "archived": false,
      "post_count": 7
    }
  ]
}

curl:

bash
curl -sS \
  "https://fedisuite.example.com/api/labels" \
  -H "Authorization: Bearer $TOKEN"

7.2 POST /api/labels

Zweck:

  • Label anlegen

Request-Body:

json
{
  "name": "Release",
  "color": "#5bc8f5"
}

Regeln:

  • name ist Pflicht, höchstens 64 Zeichen und ohne Steuerzeichen
  • color ist 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:

bash
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:

json
{
  "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:

bash
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:

json
{
  "success": true
}

curl:

bash
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:

json
{
  "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:

bash
curl -sS \
  "https://fedisuite.example.com/api/campaigns" \
  -H "Authorization: Bearer $TOKEN"

7.6 POST /api/campaigns

Zweck:

  • Kampagne anlegen

Request-Body:

json
{
  "name": "Herbstkampagne",
  "description": "Beiträge zum Herbst-Release",
  "startDate": "2026-10-01",
  "endDate": "2026-10-31",
  "labelIds": [4]
}

Regeln:

  • name ist Pflicht und hat höchstens 100 Zeichen
  • description ist optional und hat höchstens 2000 Zeichen
  • startDate und endDate sind optional im Format YYYY-MM-DD, das Ende darf nicht vor dem Start liegen (400)
  • labelIds sind die Labels, die die Kampagne mitbringt. Fremde Labels führen zu 404

Antwort: die neue Kampagne (wie ein Eintrag aus 7.5).

curl:

bash
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:

bash
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:

json
{
  "success": true
}

curl:

bash
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:

json
{
  "success": true
}

curl:

bash
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:

json
{
  "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:

bash
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:

  • days
  • limit, Standard 10, Maximum 100
  • sort=favourites_count|reblogs_count|replies_count|total_engagement|net_reach|gross_reach, Standard favourites_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_reach und reach_state
  • leere Beiträge (ohne Text und ohne Medien) werden nicht aufgeführt

curl:

bash
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, Standard 30, Maximum 730, 0 bedeutet gesamter Zeitraum

Antwort, gekürzt:

json
{
  "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:

bash
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, Standard 30, Maximum 730, 0 bedeutet gesamter Zeitraum
  • limit, Standard 20, Maximum 100
  • sort=net_reach|gross_reach|boosts|date|favourites_count|reblogs_count|replies_count, Standard net_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, partial oder complete), reach_error und reach_fetched_at
  • nur Beiträge mit der Sichtbarkeit public oder unlisted

curl:

bash
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:

  • days für den Vergleichszeitraum, ohne Angabe der gesamte Zeitraum

postId ist die fediverse_post_id des Beitrags.

Antwort, gekürzt:

json
{
  "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:

bash
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:

json
[
  {
    "date": "2026-10-01",
    "posts_count": 3,
    "total_favourites": 20,
    "total_reblogs": 5,
    "total_replies": 2
  }
]

curl:

bash
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:

json
[
  {
    "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:

bash
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_count und engagement_rate (Interaktionen pro Beitrag und Tag)

curl:

bash
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_change und followers_end

curl:

bash
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:

json
{
  "favourites": 320,
  "boosts": 74,
  "replies": 41
}

curl:

bash
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_engagement und post_count

Hinweise:

  • avg_engagement ist 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:

bash
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) und post_count

curl:

bash
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_end und net_change

curl:

bash
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" und type: "text" mit avg_engagement, post_count, avg_favourites, avg_boosts und avg_replies

curl:

bash
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_replies und post_count

curl:

bash
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 visibility und post_count, die größte Gruppe zuerst

curl:

bash
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:

json
{
  "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:

bash
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:

  • days
  • limit, Standard 12, Maximum 30
  • sort=total_engagement|avg_engagement|posts_count, Standard total_engagement

Antwort:

  • Array mit tag, posts_count, total_favourites, total_boosts, total_replies, total_engagement, avg_engagement, boost_rate und reply_rate

curl:

bash
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:

  • days
  • limit, Standard 10, Maximum 25

Antwort:

  • Array mit tag_a, tag_b, posts_count, total_engagement und avg_engagement

curl:

bash
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:

bash
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 Feature 404

Query-Parameter:

  • days, Standard 180, Maximum 730, 0 bedeutet 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_posts false und dimensions, patterns und time_windows bleiben 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_basis ist low, medium (ab 30 verglichenen Beiträgen) oder high (ab 100)
  • Sprache und Content-Warning sind nur für Beiträge bekannt, die FediSuite selbst gesendet hat

Antwort, gekürzt:

json
{
  "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[].dimension ist media, link, length, visibility, language oder content_warning, eine Dimension ohne ausreichende Datenbasis fehlt
  • versus_rest.<Maß>.reason nennt die erste Regel, an der ein Unterschied gescheitert ist (too_few_posts, no_baseline, trivial_effect, not_significant oder outlier_driven), bei bestandenen Unterschieden ist es null
  • difference ist relativ: 0.42 bedeutet 42 Prozent mehr als der Rest
  • labels[].trend vergleicht mit dem Zeitraum davor und ist null, wenn einer der beiden Zeiträume zu wenige Beiträge hat
  • time_windows[].weekday zählt von 1 (Montag) bis 7 (Sonntag), die Stunden gelten in der Zeitzone der Nutzer*in. level ist good oder above_average

curl:

bash
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:

bash
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:

json
{
  "success": true
}

curl:

bash
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 (0 ist der gesamte Zeitraum) oder null

curl:

bash
curl -sS \
  "https://fedisuite.example.com/api/user/dashboard-period" \
  -H "Authorization: Bearer $TOKEN"

9.4 PUT /api/user/dashboard-period

Request-Body:

json
{
  "period": 30
}

Erlaubt sind 0, 7, 30, 90, 365 und 730, sonst 400.

Antwort:

json
{
  "success": true
}

curl:

bash
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:

bash
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:

json
{
  "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:

json
{
  "success": true,
  "dashboard_selected_account_id": 123
}

curl:

bash
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:

json
{
  "view": "list"
}

view ist list (Standard) oder calendar.

curl:

bash
curl -sS \
  "https://fedisuite.example.com/api/user/posts-view" \
  -H "Authorization: Bearer $TOKEN"

9.8 PUT /api/user/posts-view

Request-Body:

json
{
  "view": "calendar"
}

Erlaubt sind list und calendar, sonst 400.

Antwort:

json
{
  "view": "calendar"
}

curl:

bash
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:

json
{
  "id": 42,
  "email": "user@example.com",
  "timezone": "Europe/Berlin",
  "default_account_id": 123,
  "language": "de",
  "theme": "dark"
}

curl:

bash
curl -sS \
  "https://fedisuite.example.com/api/user/profile" \
  -H "Authorization: Bearer $TOKEN"

9.10 PUT /api/user/language

Request-Body:

json
{
  "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:

json
{
  "success": true,
  "language": "de"
}

curl:

bash
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:

json
{
  "theme": "dark"
}

Erlaubt sind dark und light, sonst 400.

Antwort:

json
{
  "success": true,
  "theme": "dark"
}

curl:

bash
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:

json
{
  "newEmail": "neu@example.com",
  "currentPassword": "mein-passwort"
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "currentPassword": "altes-passwort",
  "newPassword": "neues-passwort"
}

Antwort:

json
{
  "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:

bash
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:

json
{
  "timezone": "Europe/Berlin"
}

timezone muss ein gültiger IANA-Zeitzonenname sein, sonst 400.

Antwort:

json
{
  "success": true
}

curl:

bash
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:

json
{
  "accountId": 123
}

oder zum Zurücksetzen:

json
{
  "accountId": null
}

Ein Account, der nicht der Nutzer*in gehört, führt zu 404.

Antwort:

json
{
  "success": true,
  "default_account_id": 123
}

curl:

bash
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:

json
{
  "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:

bash
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:

  • sessionId ist die ID aus 9.16 (64 Hexadezimalzeichen), sonst 400
  • unbekannte oder bereits widerrufene Sitzung: 404

Antwort:

json
{
  "success": true
}

curl:

bash
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, Dateiname fedisuite-export-JJJJ-MM-TT.zip)
  • enthalten sind auskunft.md, profil.md, konten.md, beitraege.md, verlauf.md, follower_ereignisse.md, statistiken.md und daten.json mit den Rohdaten
  • Passwörter und OAuth-Tokens sind nicht enthalten
  • die Dokumente im Archiv sind deutschsprachig

curl:

bash
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:

json
{
  "password": "mein-passwort"
}

Verhalten:

  • Passwort-Prüfung (ohne Passwort 400, falsches Passwort 403)
  • Medien-Cleanup
  • Plugin-Cleanup für alle Accounts
  • Löschung von Beiträgen, Accounts und Nutzer*in in einer Transaktion

Antwort:

json
{
  "success": true
}

curl:

bash
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_time
  • public_config, mit enableUserRegistration, appName und publicSiteUrl (die Feature-Schalter stehen in GET /api/public/config und GET /api/mobile/public/info)
  • notice
  • user, mit is_admin
  • summary, 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_errors und weitere)
  • accounts
  • mobile_capabilities

Wichtige Capabilities:

  • dashboard_periods: aktuell [7, 30, 90, 365, 730, 0]
  • top_post_sort_options
  • top_hashtag_sort_options
  • supports_preferences_batch_update: true

curl:

bash
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:

  • days
  • topPostsLimit
  • topPostsSort
  • topHashtagsLimit
  • topHashtagsSort
  • hashtagCombinationsLimit

Defaults:

  • days=30, 0 bedeutet gesamter Zeitraum
  • topPostsLimit=5 (1 bis 25)
  • topHashtagsLimit=12 (1 bis 25)
  • hashtagCombinationsLimit=10 (1 bis 25)
  • topPostsSort=total_engagement
  • topHashtagsSort=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_time
  • period
  • account, mit den Zählern scheduled_posts_count, failed_posts_count, published_posts_count, draft_posts_count und den Feldern des letzten Imports (latest_import_*)
  • summary
  • charts
  • top_posts
  • insights

Besonderheiten:

  • best_times und weekday_engagement werden im Bundle in der Zeitzone der Nutzer*in berechnet
  • effective_statuses_count basiert auf GREATEST(stats_statuses, indexed_posts_count)
  • ein unbekannter Account führt zu 404, eine ungültige ID zu 400

curl:

bash
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:

  • language
  • theme
  • timezone
  • defaultAccountId

Ohne eines dieser Felder antwortet die API mit 400. Die Felder werden wie bei den Einzel-Endpunkten geprüft.

Antwort:

json
{
  "success": true,
  "profile": {
    "id": 42,
    "email": "user@example.com",
    "timezone": "Europe/Berlin",
    "default_account_id": 123,
    "language": "de",
    "theme": "dark"
  }
}

curl:

bash
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:

bash
BASE_URL="https://fedisuite.example.com"

Health prüfen:

bash
curl -sS "$BASE_URL/api/health"

Login:

bash
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):

bash
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:

bash
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:

bash
curl -sS "$BASE_URL/api/mobile/bootstrap" \
  -H "Authorization: Bearer $TOKEN"

Accounts laden:

bash
curl -sS "$BASE_URL/api/accounts" \
  -H "Authorization: Bearer $TOKEN"

Dashboard laden:

bash
curl -sS "$BASE_URL/api/mobile/accounts/123/dashboard?days=30" \
  -H "Authorization: Bearer $TOKEN"

Warteschlange laden:

bash
curl -sS "$BASE_URL/api/posts?status=scheduled" \
  -H "Authorization: Bearer $TOKEN"

Geplanten Beitrag anlegen:

bash
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:

bash
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:

bash
curl -sS \
  "$BASE_URL/api/posts/calendar?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z" \
  -H "Authorization: Bearer $TOKEN"