Instagram (Content-Analyse)
Instagram-Profi-Konten per Login verbinden, Beiträge und Insights lesen — für den eigenen Workspace oder als Partner-App über die API.
TaskMonkey liest Instagram-Profi-Konten (Business oder Creator) über die Instagram API with Instagram Login (graph.instagram.com). Nur lesend: Profil, Beiträge, Beitrags- und Konto-Insights. Es wird nichts gepostet, keine Kommentare oder Direktnachrichten gelesen.
Zwei Wege:
- Eigener Workspace — Verbindungen (
/manage/connections, Karte „Instagram, Facebook, WhatsApp“) → Instagram verbinden (/manage/instagramleitet dorthin um). Danach nutzen Assistenten die Tools, z. B. Content-Analyse. - Partner-App — eine externe App schickt ihre Nutzer auf einen vom Partner-Server signierten Link
https://taskmonkey.de/connect/instagram?partner=<key>&ref=<nutzer-id>&exp=<unix>&sig=<hmac>(siehe Links signieren). Die Nutzer brauchen kein TaskMonkey-Konto. Die App holt die Daten danach per API-Key mitrefab.
Berechtigungen
| Scope | Wofür |
|---|---|
instagram_business_basic |
Profil (Benutzername, Kontotyp, Follower, Beitragszahl) und Beitragsliste |
instagram_business_manage_insights |
Kennzahlen je Beitrag und Konto (Reichweite, Aufrufe, Interaktionen …) |
Private Konten werden abgewiesen („Profi-Konto erforderlich“) — Umstellung in der Instagram-App unter Einstellungen → Kontotyp und Tools. Werden im Dialog nicht beide Berechtigungen erteilt, wird nichts gespeichert („Berechtigungen fehlen“, Rücksprung mit reason=missing_scopes).
Ablauf
- Start:
/manage/instagram/connect(Workspace, POST) bzw./connect/instagram?partner=…&ref=…&exp=…&sig=…[&redirect_url=…](Partner, Signatur wird geprüft). Die Partner-Seite erklärt vorher: „Ihre Instagram-Daten werden über TaskMonkey für <Partner> ausgewertet“. - Instagram-Dialog
https://www.instagram.com/oauth/authorizemit signiertemstate(HMAC, 15 min, an eine Cookie-Nonce im Browser gebunden, serverseitig nur einmal verwendbar). - Callback
https://app.taskmonkey.de/connect/instagram/callback: Code → kurzlebiger Token (api.instagram.com/oauth/access_token) → Long-Lived-Token, 60 Tage (graph.instagram.com/access_token,ig_exchange_token) →/me(Konto-ID, Benutzername, Kontotyp). - Speichern in
instagram_connections— Token verschlüsselt (OAuthTokenCrypto, SchlüsselOAUTH_TOKEN_KEY). Webhookinstagram.connected. - Rücksprung: Partner →
redirect_url?ref=<ref>&status=connected|denied|error[&reason=…], Workspace →/manage/instagram(→ Verbindungen).
Token-Verlängerung: bin/cake instagram_refresh läuft täglich um 04:00 (cronjobs.sh) und verlängert alle Tokens, die in weniger als 30 Tagen ablaufen (ig_refresh_token, frühestens 24 h nach Ausgabe). Abgelaufene oder ungültige Tokens werden gelöscht, die Verbindung steht auf expired, Webhook instagram.revoked.
Tools
Alle in config/tenants/_shared/tools/instagram/, für jeden Workspace verfügbar, nur lesend, per API-Key aufrufbar (external_api).
| Tool | Liefert |
|---|---|
instagramGetProfile |
Benutzername, Name, Kontotyp, Follower, Gefolgt, Beitragszahl |
instagramListMedia |
Beiträge (neueste zuerst) mit Typ, Bildunterschrift, Datum, Likes, Kommentare, Link; limit (1–100), after → next_cursor |
instagramGetMediaInsights |
Kennzahlen eines Beitrags: media_id, media_product_type (FEED/REELS/STORY), optional metrics |
instagramGetAccountInsights |
Konto-Summen für max. 30 Tage (since/until, Standard 28 Tage), optional breakdown=media_product_type |
Welche Verbindung? Ohne ref die eigene des Workspaces (bei mehreren: account=<benutzername>). Mit ref die Verbindung eines Partner-Nutzers — nur in dem Workspace, dem der Partner gehört.
Auth im ToolRunner: apis.instagram.auth.type = instagram_connection (App\Tooling\Auth\InstagramConnectionAuth). Der Token kommt aus der Verbindung, nie aus Config oder Args, geht nur an https://graph.instagram.com und wird aus Antworten entfernt (Metas Paging-Links enthalten ihn).
Hinweise von Meta: Kennzahlen können bis zu 48 h nachlaufen; einige gibt es erst ab 100 Followern; Stories nur 24 h; impressions, plays, video_views sind abgekündigt (stattdessen views).
Assistant „Content-Analyse“
_shared/assistants/instagram_content_analyse.php, standardmäßig versteckt, im meco-Workspace sichtbar. Liest die letzten 30 Beiträge plus Insights und liefert Kurzfazit, Top/Flop-Tabelle, Muster (Format, Uhrzeit, Einstieg/Hook, Hashtags, Länge/CTA) und 3–5 Vorschläge. Aktivieren in einem Workspace:
return ['assistants.instagram_content_analyse.hidden' => false];
Der Prompt ist Config (assistants.instagram_content_analyse.prompt) und lässt sich pro Workspace überschreiben.
Partner einrichten
Partner sind Plattform-Config (config/instagram.php, Instagram.partners), nicht Workspace-Config — ein Workspace kann sich nicht selbst zum Partner machen oder Rücksprung-Ziele eintragen.
'partners' => [
'acme' => [
'name' => 'Acme Creator Studio', // steht auf der Erklärseite
'tenant' => 'acme', // Workspace des Partners
'redirect_urls' => ['https://app.acme.example/instagram/done'], // exakt, https
'secret_env' => 'INSTAGRAM_PARTNER_ACME_SECRET', // Link-Secret, Wert in config/.env
'enabled' => true,
],
],
Dann im Partner-Workspace:
- API-Key anlegen (
/manage/api-keys) mit den vier Instagram-Tools. - Webhook anlegen (
/manage/webhooks) mit den Eventsinstagram.connectedundinstagram.revoked. - Link-Secret erzeugen (
openssl rand -hex 32), in die Plattform-.envalsINSTAGRAM_PARTNER_ACME_SECRET=…eintragen und dem Partner auf sicherem Weg geben. Ohne Secret weist TaskMonkey alle Links des Partners ab (503). - Nutzer-Link: vom Partner-Server pro Nutzer signiert (
ref: 1–128 ZeichenA-Z a-z 0-9 _ . : @ -), siehe unten.
Links signieren
Der Link darf nur auf dem Server des Partners entstehen (Secret nie in App oder Browser). Ohne gültige Signatur könnte jeder einen Link mit fremder ref bauen und die Instagram-Verbindung eines anderen Nutzers überschreiben.
exp = jetzt + max. 24 h (Unix-Sekunden, empfohlen: 10–30 min)
sig = hex( HMAC-SHA256( "v1\n" + partner + "\n" + ref + "\n" + exp , Link-Secret ) )
https://taskmonkey.de/connect/instagram?partner=acme&ref=user-42&exp=1767225600&sig=<64 hex>[&redirect_url=…]
PHP:
$exp = time() + 900;
$sig = hash_hmac('sha256', "v1\n{$partner}\n{$ref}\n{$exp}", getenv('TASKMONKEY_IG_LINK_SECRET'));
$url = 'https://taskmonkey.de/connect/instagram?' . http_build_query(compact('partner', 'ref', 'exp', 'sig'));
Node.js:
const exp = Math.floor(Date.now() / 1000) + 900;
const sig = crypto.createHmac('sha256', process.env.TASKMONKEY_IG_LINK_SECRET)
.update(`v1\n${partner}\n${ref}\n${exp}`).digest('hex');
Abgewiesen (400, kein Instagram-Dialog): fehlende/falsche Signatur, andere ref als signiert, exp vorbei („Link abgelaufen“) oder mehr als 24 h in der Zukunft. redirect_url ist nicht Teil der Signatur, muss aber exakt in redirect_urls stehen. Secret-Rotation: neues Secret in .env, Partner stellt um — alte Links sind sofort ungültig.
Abruf:
curl -X POST https://app.taskmonkey.de/api/v1/tools/instagramListMedia \
-H 'Authorization: Bearer tm_live_…' -H 'Content-Type: application/json' \
-d '{"args":{"ref":"user-42","limit":25}}'
Isolation: Der Workspace kommt aus dem API-Key. ref wählt nur unter den Verbindungen, deren Partner laut Plattform-Config zu genau diesem Workspace gehört. Gleiche ref bei zwei Partnern = zwei getrennte Verbindungen.
Webhook-Daten (data): connection_id, partner, ref, ig_user_id, username; bei instagram.connected zusätzlich account_type, scopes, reconnected; bei instagram.revoked reason (deauthorized, data_deletion, disconnected, expired, token_invalid).
Meta-Pflicht-Callbacks
| URL | Methode | Wirkung |
|---|---|---|
/connect/instagram/deauthorize |
POST signed_request |
Nutzer hat die App in Instagram entfernt: Token löschen, Status revoked, Webhook |
/connect/instagram/data-deletion |
POST signed_request |
Datenlöschung: Webhook, Verbindung löschen, Antwort {"url": …, "confirmation_code": …} |
/connect/instagram/deletion-status/<code> |
GET | Status der Löschung für den Nutzer |
/connect/instagram/deauthorize, /connect/instagram/data-deletion |
GET/HEAD | Infoseite (200): wie man die Löschung anstößt, Link zur Datenschutzerklärung. Keine Wirkung. Metas Crawler prüft die eingetragenen URLs per GET |
signed_request wird per HMAC-SHA256 nur gegen INSTAGRAM_APP_SECRET geprüft (nicht gegen das Facebook-App-Secret META_APP_SECRET), dazu issued_at (Pflicht, max. 24 h alt). Ohne gültige Signatur: 403, keine Wirkung.
Einrichtung (Betreiber)
Plattform-.env (config/.env):
INSTAGRAM_APP_ID=… # Meta-App → Instagram → API-Setup mit Instagram-Business-Login
INSTAGRAM_APP_SECRET=…
Meta-App-Review und einzutragende URLs: docs/plans/instagram-app-review.md.
Technik
| Was | Wo |
|---|---|
| Plattform-Config | config/instagram.php |
| OAuth, Callbacks | src/Controller/ConnectInstagramController.php, templates/ConnectInstagram/ |
| Workspace-UI | src/Controller/Manage/InstagramController.php |
| Lebenszyklus | src/Service/Instagram/InstagramConnectionService.php (+ InstagramGraphClient, InstagramOAuthState, InstagramPartnerLink, SignedRequest) |
| Tool-Auth | src/Tooling/Auth/InstagramConnectionAuth.php |
| Refresh | bin/cake instagram_refresh |
| Tabellen | instagram_connections, instagram_deletion_requests |