Schnellstart für Shagers REST-API: vom frischen Schlüssel zum getrackten Beitrag auf 40 Konten in sieben Aufrufen
Shagers REST-API in curl und TypeScript: Anmeldung, Workspaces, Kanäle, Medien, Entwürfe pro Konto, Planung, Statusabfrage und Klicks.

Shagers REST-API liegt unter https://app.shager.ai/api/v1, authentifiziert mit Authorization: Bearer shgr_live_..., liefert jedes Ergebnis als { "data": ... } und jeden Fehler als { "error": { "code", "message" } }. Ein kompletter Verteilungsdurchlauf besteht aus sieben Aufrufen: Schlüssel prüfen, Workspace wählen, Kanäle auflisten, Medien hochladen, einen Entwurf mit Texten pro Konto anlegen, ihn planen, dann Status und Klicks abfragen. Die maschinenlesbare Spezifikation ist das OpenAPI-3.1-Dokument.
Alles Folgende läuft genau so, wie es dasteht, sobald du zwei Umgebungsvariablen gesetzt hast. Dieselben Operationen gibt es als MCP-Tools mit denselben Namen – was du hier lernst, gilt also auch für Claude Code, Codex oder Cursor.
Was brauchst du vor dem ersten Aufruf?
Einen Schlüssel. Entweder erstellt ihn ein Mensch unter https://app.shager.ai/dashboard/agents mit den Scopes, die du brauchst, oder dein Agent legt per Selbstregistrierung eine Sandbox an. Die Scopes funktionieren so:
| Scope | Erlaubt |
|---|---|
read |
whoami, Workspaces, Kanäle, Gruppen und Beiträge auflisten, einen Beitrag lesen, Analytics |
write |
Workspaces, Gruppen, Medien, Entwürfe, Verbindungslinks und Freigabeanfragen anlegen, stornieren |
publish |
Planen, einschließlich create_post mit schedule_at |
export SHAGER_API_KEY="shgr_live_your_key_here"
export API="https://app.shager.ai/api/v1"

Aufruf 1: Wer bin ich?
curl -s "$API/me" -H "Authorization: Bearer $SHAGER_API_KEY"
{ "data": { "organization_id": "...", "organization": "Northwind Agency", "sandbox": false, "scopes": ["read", "write"] } }
Ist sandbox true, sind Planen und Verbindungslinks gesperrt, bis ein Mensch den Schlüssel übernimmt. Fehlt publish in scopes, kannst du Entwürfe bauen, aber nicht planen. Wer das vorher weiß, erspart sich später einen rätselhaften 403.
Aufruf 2: Workspace wählen oder anlegen
Ein Workspace ist meist ein Kunde oder eine Marke.
curl -s "$API/workspaces" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s -X POST "$API/workspaces" \
-H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
-d '{"name": "Summit Physio", "timezone": "Europe/London", "approval_required": true}'
Setz beim Anlegen immer timezone (einen IANA-Namen). Geplante Zeiten werden mit ihrem Offset gespeichert, und über die Zone des Workspace zeigt Shager sie Menschen an. approval_required: true bedeutet, dass der Kunde Beiträge freigeben muss, bevor sie geplant werden können – ein guter Standard für neue Kunden.
Exportiere die ID aus der Antwort, damit die folgenden Befehle funktionieren:
export WS="the_workspace_id"
Aufruf 3: Kanäle und Gruppen auflisten
curl -s "$API/workspaces/$WS/channels" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/groups" -H "Authorization: Bearer $SHAGER_API_KEY"
Jeder Kanal kommt mit id, network, name, handle, status, den unterstützten formats und connected_by_client zurück. Gruppen liefern id, name und channel_ids. Über Gruppen sprichst du vierzig Konten an, ohne vierzig IDs aufzuzählen: Übergib group_ids an create_post, und jeder aktive Kanal der Gruppe ist dabei.
Hat der Workspace noch keine Kanäle, erstelle mit POST /workspaces/{workspace_id}/connect-links einen Verbindungslink und schick ihn an den Kontoinhaber. Der verbindet Facebook, Instagram, YouTube, X, LinkedIn oder TikTok selbst, ganz ohne Shager-Login.

Aufruf 4: Medien von einer URL hochladen
curl -s -X POST "$API/workspaces/$WS/media" \
-H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
-d '{
"url": "https://cdn.summitphysio.example/video/knee-mobility.mp4",
"file_name": "knee-mobility.mp4",
"width": 1080, "height": 1920, "duration_ms": 42000
}'
Shager lädt die Datei serverseitig herunter und gibt id, kind, mime_type und size_bytes zurück. Übergib für Reels, Shorts und Storys width, height und duration_ms, wenn du sie kennst. So kann Shager die Formatregeln jedes Netzwerks prüfen, bevor ein Beitrag geplant wird, statt erst beim Versand zu scheitern.
Aufruf 5: einen Entwurf mit Texten pro Konto anlegen
Das ist der entscheidende Aufruf. Hier in TypeScript mit schlichtem fetch, inklusive einer kleinen Hilfsfunktion, die du wiederverwenden wirst:
const API = "https://app.shager.ai/api/v1";
async function shager<T>(method: string, path: string, body?: unknown): Promise<T> {
const res = await fetch(`${API}${path}`, {
method,
headers: {
Authorization: `Bearer ${process.env.SHAGER_API_KEY}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json();
if (!res.ok) {
const err = new Error(`${res.status} ${json.error.code}: ${json.error.message}`);
(err as any).status = res.status;
throw err;
}
return json.data as T;
}
const WS = process.env.SHAGER_WORKSPACE_ID!;
const mediaId = process.env.SHAGER_MEDIA_ID!; // the id returned by call 4
const channels = await shager<Array<{ id: string; name: string; network: string; status: string }>>(
"GET", `/workspaces/${WS}/channels`,
);
const post = await shager<{ id: string; status: string }>("POST", `/workspaces/${WS}/posts`, {
label: "Knee mobility, week 42",
caption: "Three minutes a day for stiffer knees. Full routine in the video.",
channel_ids: channels.map((c) => c.id),
media_ids: [mediaId],
format: "REEL",
youtube_title: "3-minute knee mobility routine",
youtube_privacy: "public",
per_channel: channels.map((c) => ({
channel_id: c.id,
caption: `${c.name}: three minutes a day for stiffer knees. Our physios show the routine.`,
})),
cta_url: "https://summitphysio.example/book",
cta_text: "Book an assessment",
campaign: "knee-mobility-oct",
first_comment: "Book an assessment: https://summitphysio.example/book",
stagger_seconds: [45, 120],
});
console.log(post.id, post.status); // DRAFT
Was dir jedes Feld bringt:
per_channelgibt jedem Konto seinen eigenen Text. In der Praxis schreibt die ein Agent; die Vorlage oben ist nur ein Platzhalter für diesen Schritt. Die Begründung steht in Spam-Sperren in großem Maßstab vermeiden.formatwird angewendet, wo das Netzwerk es unterstützt; ansonsten wirdSHORToderREELpro Netzwerk aufgelöst.youtube_titleundyoutube_privacywirken sich nur auf YouTube-Kanäle aus.cta_urlerzeugt pro Konto einen eigenen Kurzlink und einen eigenen UTM-Satz, wobeiutm_contentdas Konto kennzeichnet. Darauf baut die Lead-Zuordnung pro Konto auf.stagger_secondslegt eine zufällige Pause zwischen Konten desselben Netzwerks fest. Standard ist[30, 90].
Ohne schedule_at entsteht ein Entwurf. Merk dir seine ID für die nächsten Aufrufe mit export POST_ID="the_post_id".
Aufruf 6: Freigabe oder Planung
Verlangt der Workspace eine Freigabe, schick den Beitrag an den Kunden. Die Antwort enthält eine approval_url, die der Prüfer ohne Login öffnet:
curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/approval" \
-H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
-d '{"reviewer_name": "Dana Ellis", "reviewer_email": "dana@summitphysio.example", "send_email": true}'
Nach der Freigabe (oder wenn keine nötig ist) planst du den Beitrag. Dafür braucht es den Scope publish, und schedule_at muss ISO 8601 mit Offset sein:
curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/schedule" \
-H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
-d '{"schedule_at": "2026-10-20T08:30:00+01:00"}'
An dieser Stelle prüft Shager jedes Ziel gegen die Regeln seines Netzwerks. Würde ein Konto den Beitrag ablehnen, bekommst du einen 422 rejected mit Begründung, und nichts landet in der Warteschlange.
Aufruf 7: Status und Klicks abfragen
curl -s "$API/workspaces/$WS/posts/$POST_ID" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/posts?status=SCHEDULED&limit=50" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/analytics?from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z" \
-H "Authorization: Bearer $SHAGER_API_KEY"
get_post liefert eine Zeile pro Ziel mit state, dispatch_at, published_at, der Live-url und gegebenenfalls einem error. Analytics liefert total_clicks, by_account und by_post und zählt die Klicks auf Shagers Tracking-Links, Bot-Traffic ausgenommen. Einen geplanten Beitrag, dessen Veröffentlichung noch nicht begonnen hat, stornierst du mit POST /workspaces/{workspace_id}/posts/{post_id}/cancel.
Welche Fehler sollte dein Code abfangen?
| Status und Code | Bedeutung | Was tun |
|---|---|---|
401 unauthorized |
Header fehlt oder ist fehlerhaft | Authorization: Bearer shgr_... senden |
403 forbidden |
Dem Schlüssel fehlt der nötige Scope | Stattdessen einen Entwurf anlegen oder einen Menschen um publish bitten |
403 sandbox_unclaimed |
Nicht übernommener Schlüssel aus der Selbstregistrierung | Den Übernahme-Link einem Menschen zeigen |
404 not_found |
Falsche Workspace-, Beitrags- oder Kanal-ID | Neu auflisten; IDs gelten pro Workspace |
409 not_scheduled |
Stornierung eines Beitrags, der nicht geplant ist | Zuerst den Status des Beitrags lesen |
422 rejected |
Eine Netzwerkregel ist verletzt | Die Meldung anzeigen; sie benennt das Problem |
429 rate_limited |
Mehr als 120 Anfragen pro Minute und Schlüssel | Kurz warten und erneut versuchen |
Ein gut gebauter Client stößt selten ans Rate-Limit. Ein einziges create_post deckt Hunderte Konten ab, du brauchst also keinen Aufruf pro Konto.
FAQ
Gibt es ein SDK?
Kein eigenes. Das OpenAPI-3.1-Dokument funktioniert mit gängigen Generatoren, falls du typisierte Clients willst, und die API ist so überschaubar, dass eine Hilfsfunktion wie die oben meist völlig reicht. Eine Shager-CLI ist in Arbeit.
Sind REST und MCP wirklich dasselbe?
Ja. Jedes MCP-Tool entspricht genau einer REST-Operation mit demselben Namen, denselben Eingaben, Scopes, demselben Rate-Limit und derselben Audit-Protokollierung. Nutze REST aus Skripten und Diensten, MCP aus Agenten-Clients. Die vollständige Tool-Liste steht auf der Seite für Agenten.
Wie poste ich im selben Beitrag unterschiedlichen Text auf Facebook und YouTube?
Mit per_channel, jeweils mit ID und Text des Kanals, und mit youtube_title für den YouTube-Titel. Ein Beitragsobjekt, viele Ziele – jedes mit eigenem Text, eigenem Format und, falls nötig, eigenem ersten Kommentar.
Nächster Schritt
Wenn diese Abfolge von Hand sauber läuft, pack sie in etwas, das ein Agent wiederholen kann. Einen Claude-Skill für Shager bauen zeigt, wie du diese Aufrufe und die Regeln drumherum in einen wiederverwendbaren Skill packst.


