Übersicht

17. Instagram 🚧

Implementiert, aber ohne konfigurierte Meta-App noch nicht produktiv nutzbar. Der gesamte Code-Pfad (Konto-Verbindung, Content Studio, Kommentar-zu-DM-Automation, Inbox, CRM-VerknĂŒpfung) ist gebaut, getestet und lauffĂ€hig — was fehlt, ist eine echte, im Meta Developer Dashboard angelegte und von Meta freigegebene App (INSTAGRAM_APP_ID/ INSTAGRAM_APP_SECRET/INSTAGRAM_OAUTH_STATE_SECRET/INSTAGRAM_VERIFY_TOKEN als Umgebungsvariablen). Solange diese fehlen, zeigt die OberflĂ€che ehrlich „Meta-Verbindung erforderlich"/„Berechtigung fehlt" statt eine Verbindung oder Veröffentlichung vorzutĂ€uschen.

Die Verbindung lĂ€uft ĂŒber den direkten Instagram-Login (Business Login for Instagram) — keine Facebook-Seite nötig. Das ersetzt einen frĂŒheren Facebook-Login-for-Business-Ansatz vollstĂ€ndig.

Was existiert

BereichRouteFunktion
Konto verbinden/instagram/einstellungenDirekter Instagram-Login, Status/Token/Berechtigungsanzeige, Trennen
Content erstellen/instagramÜbersicht aller EntwĂŒrfe (Filter/Suche/Sortierung), „Neuer Entwurf"
Entwurf bearbeiten/instagram/entwurf/:idMedien-Upload, Caption/Hashtags/CTA, KI-Vorbereitung (echt + Mock-Vorschau), Live-Vorschau, Speichern/Planen/Veröffentlichen
Content Kalender/instagram/kalenderMonatskalender + Liste geplanter BeitrÀge
Veröffentlichte BeitrÀge/instagram/veroeffentlichtListe bereits veröffentlichter BeitrÀge
Automationen/instagram/automationen, /instagram/automationen/:idKommentar-zu-DM-Automation pro Beitrag/Reel: Keyword-Regeln, Antworttext mit Variablen, PDF-Auswahl, Live-DM-Vorschau, Ereignis-Logs
Inbox/instagram/inboxAlle Kommentar-/DM-Ereignisse, Filter nach Status/Automation, Verlinkung zum verknĂŒpften CRM-Kontakt

Backend: src/lib/instagram-*.server.ts/*.functions.ts, OAuth-Callback unter /api/public/oauth/instagram/callback, Webhook-Empfang unter /api/public/instagram/webhook, Datenmodell in den Migrationen 20261103090000_..._instagramkern001.sql, 20261104090000_..._instagramcontentstudio001.sql, 20261105090000_..._instagramcrminbox001.sql.

Berechtigungen (direkter Instagram-Login)

PRONTO nutzt fĂŒr Instagram eine eigene, von WhatsApp getrennte Meta-App (eigene App-Review-Einreichung, eigene Env-Vars). Angeforderte Scopes und wofĂŒr sie jeweils gebraucht werden — keine pages_*-Scopes mehr nötig, da keine Facebook-Seite mehr im Spiel ist:

BerechtigungWofĂŒr
instagram_business_basicGrundzugriff auf das verbundene Instagram-Professional-Konto (Username, Account-ID, Profilbild)
instagram_business_manage_commentsEingehende Kommentare per Webhook empfangen, fĂŒr die Keyword-Automation lesen, private Antworten auf Kommentare senden
instagram_business_manage_messagesDirektnachrichten allgemein — Webhook-Feld messages (Quick-Reply-Button-Klicks empfangen) und Folge-DMs im Buttons-Antwortmodus senden
instagram_business_content_publishBeitrÀge direkt aus dem Content Studio auf Instagram veröffentlichen

Keine dieser Berechtigungen wird fĂŒr Funktionen angefordert, die sie nicht tatsĂ€chlich brauchen — insbesondere: kein instagram_business_manage_insights, kein Zugriff auf Facebook-Seiten-Inhalte (es gibt keine mehr im Spiel).

OAuth-Flow (direkter Instagram-Login, kein Facebook JS SDK)

Klassischer serverseitiger Redirect-Flow — strukturell identisch zur bestehenden Google-/Zoom-Anbindung (google-oauth.server.ts), nicht mehr zum WhatsApp-Popup-Muster:

  1. „Mit Instagram verbinden" auf /instagram/einstellungen ruft startInstagramConnect auf (liefert eine signierte, 10 Minuten gĂŒltige state-URL) und leitet den Browser direkt auf https://www.instagram.com/oauth/authorize weiter — kein SDK, kein Popup.
  2. Instagram leitet nach Zustimmung auf /api/public/oauth/instagram/callback?code=
 zurĂŒck.
  3. Server tauscht den Code serverseitig gegen einen kurzlebigen (~1 Std.) Instagram- User-Token — liefert direkt die Instagram-User-ID und die tatsĂ€chlich gewĂ€hrten Berechtigungen mit (kein separater debug_token-Call mehr nötig).
  4. Server tauscht gegen einen langlebigen Token (~60 Tage).
  5. Server liest Username/Profilbild direkt vom Instagram-Account (kein Umweg ĂŒber Facebook-Seiten mehr) und meldet das Konto fĂŒr Kommentar-Webhooks an (POST graph.instagram.com/{ig-user-id}/subscribed_apps).
  6. Token wird unverschlĂŒsselt in instagram_accounts gespeichert — siehe Sicherheitshinweis unten.
  7. „Verbindung testen" prĂŒft den gespeicherten Token per Live-API-Call, „Trennen" löscht ihn und markiert den Datensatz als disconnected.

Alle Daten-/Content-/Messaging-Calls laufen ĂŒber den Host graph.instagram.com (statt graph.facebook.com beim vorherigen Facebook-Login-Ansatz).

Webhook-Flow

POST /api/public/instagram/webhook (Feld comments, siehe instagram-webhook.server.ts) — unverĂ€ndert gegenĂŒber dem vorherigen Ansatz, da das Webhook-Payload-Format login-methoden-unabhĂ€ngig ist:

  1. Meta-GET-Verifizierung gegen INSTAGRAM_VERIFY_TOKEN beim Einrichten der Subscription.
  2. Jeder eingehende POST wird zuerst roh und idempotent in instagram_webhook_events gespeichert (Unique-Constraint auf einen Payload-Hash), bevor irgendetwas verarbeitet wird — schĂŒtzt vor Replay/Doppelverarbeitung bei Meta-Retries.
  3. Pro Kommentar-Change einzeln verarbeitet (ein defekter Eintrag blockiert nie den Rest desselben Aufrufs); eigene Kommentare des Kontos werden ignoriert (keine Endlosschleife).
  4. Bei Keyword-Treffer gegen eine aktive Automation: CRM-VerknĂŒpfung (instagram-crm.server.ts) → DM-Versand (instagram-dm-versand.server.ts, Credit-Abzug vor dem Versand) → Ereignis-Status wird ehrlich fortgeschrieben (sent/failed/ permission_missing/blocked/rate_limited — nie ein vorgetĂ€uschter Erfolg).

Die Webhook-Anmeldung selbst lĂ€uft jetzt anders als zuvor: statt einer rein manuellen Konfiguration auf Facebook-Seiten-Ebene im Meta-Dashboard meldet PRONTO jedes verbundene Konto direkt per API an (abonniereWebhook() in instagram-oauth.server.ts, Schritt 5 im OAuth-Flow oben) — FehlschlĂ€ge dabei blockieren die Verbindung nicht, werden der Verbindungskarte aber als Warnung angezeigt.

Private Reply / DM-Versand

sendeInstagramPrivateReply() (instagram-dm.server.ts) ruft POST https://graph.instagram.com/{ig-user-id}/messages mit {"recipient":{"comment_id":"
"},"message":{"text":"
"}} auf — echte Implementierung, kein Platzhalter: liefert "sent" ausschließlich, wenn Meta den Versand mit einer message_id bestĂ€tigt. Fehler werden nach Status/Meldung eingeordnet (Berechtigung fehlt, 7-Tage-Antwortfenster abgelaufen, Rate-Limit, sonstiger Fehler) — nie stillschweigend als Erfolg gewertet.

Antwortmöglichkeiten (Buttons) statt eines einzelnen Texts

Jede Automation kann statt eines einzelnen Antworttexts mehrere auswÀhlbare Antwortmöglichkeiten anbieten (instagram_automations.antwort_modus = 'buttons', Tabelle instagram_automation_antworten, UI unter /instagram/automationen/$id). Jede Option hat einen eigenen Button-Titel (max. 20 Zeichen), einen eigenen Antworttext und einen eigenen Inhalt (Text/PDF/Link/Bild).

Technisch nutzt das Instagrams Quick Replies (offiziell dokumentiert unter developers.facebook.com/docs/messenger-platform/instagram/features/quick-replies, max. 13 Buttons):

  1. Bei Keyword-Treffer sendet sendeInstagramQuickReplies() (instagram-dm.server.ts) eine Private Reply mit message.quick_replies (ein Eintrag pro Antwortmöglichkeit, payload = die jeweilige instagram_automation_antworten.id — opak, keine zusĂ€tzliche Zuordnungstabelle nötig).
  2. Klickt der Nutzer einen Button, sendet Meta ein Webhook-Event im Feld messaging (nicht comments) mit message.quick_reply.payload — dafĂŒr ist die Webhook-Subscription jetzt auf subscribed_fields=comments,messages erweitert (abonniereWebhook()), was die Berechtigung instagram_business_manage_messages voraussetzt.
  3. verarbeiteEinzelnenButtonKlick() (instagram-webhook.server.ts) liest die Option per ID, lĂ€dt ihren Inhalt (signierter PDF-/Bild-Link oder freier Link) und schickt ihn per sendeInstagramDirectMessage() als Folge-DM direkt an den Absender (recipient.id, nicht recipient.comment_id — dafĂŒr gilt Metas allgemeines 24-Stunden-Antwortfenster, nicht das 7-Tage-Fenster der ursprĂŒnglichen Kommentarantwort).

FĂŒr den App-Review-Tester: TestfĂ€lle

  1. Instagram-Professional-Konto unter /instagram/einstellungen direkt verbinden (keine Facebook-Seite nötig) — Status wechselt auf „Verbunden", zeigt Username/Profilbild/Account-ID/erteilte Berechtigungen.
  2. Unter /instagram/automationen „Neue Automation" → Beitrag/Reel auswĂ€hlen (Button „Von Instagram laden" synchronisiert die Post-Liste ĂŒber die Graph API) → Keyword (z. B. „PDF") + Antworttext + optional PDF hinterlegen → Status auf „Aktiv" setzen.
  3. Als Test-Nutzer einen Kommentar mit dem Keyword auf dem gewÀhlten Beitrag hinterlassen.
  4. Innerhalb weniger Sekunden erscheint eine private Instagram-Direktnachricht mit dem konfigurierten Text/Link beim kommentierenden Nutzer.
  5. In PRONTO unter /instagram/inbox erscheint das Ereignis mit Status „Gesendet", der erkannten Keyword, und einem Link zum automatisch angelegten CRM-Kontakt.
  6. Unter /instagram/automationen/:id lĂ€sst sich die Automation jederzeit pausieren („Pausieren"-Button) — danach reagiert sie auf neue Kommentare nicht mehr.
  7. Content Studio: unter /instagram einen Entwurf mit Bild + Caption anlegen, unter /instagram/entwurf/:id „Direkt veröffentlichen" — landet als echter Beitrag auf dem verbundenen Instagram-Konto (nur Einzelbild-Format automatisch unterstĂŒtzt, siehe unten).

Bekannte EinschrÀnkungen

  • Nur Einzelbild-Veröffentlichung ist ĂŒber die Graph API automatisiert umgesetzt; Carousel/Reel/Story zeigen im Content Studio einen klaren Hinweis, dass die Veröffentlichung manuell auf Instagram erfolgen muss (mehrstufige Container/ Video-Verarbeitung wĂ€ren ein eigenstĂ€ndiger, grĂ¶ĂŸerer Ausbauschritt).
  • Token-VerschlĂŒsselung: Access-Tokens liegen wie beim bestehenden WhatsApp-Modul im Klartext in der Datenbank (RLS + ausschließlich service_role-Zugriff, nie ans Frontend ausgegeben) — eine echte VerschlĂŒsselungsschicht existiert projektweit nicht und wurde hier bewusst nicht isoliert nur fĂŒr Instagram nachgerĂŒstet (siehe 25-security.md).
  • Kein automatischer Token-Refresh: aktualisiereInstagramToken() (Refresh-Funktion) ist implementiert, aber noch nicht an einen Cron-Job angebunden — lĂ€uft der langlebige Token nach ~60 Tagen ab, zeigt die Verbindungskarte „Verbindung abgelaufen", Nutzer verbinden manuell neu (identisch zum bestehenden WhatsApp-Verhalten).

Datenschutz-/Nutzungsbedingungen-Anforderungen (fĂŒr die App-Review-Einreichung)

Meta verlangt bei der App-Review-Einreichung fĂŒr jede der oben genannten Berechtigungen:

  • Eine öffentlich erreichbare DatenschutzerklĂ€rung, die explizit beschreibt, dass Instagram-Kommentartext, Nutzername und Nutzer-ID verarbeitet werden, um automatisierte Direktnachrichten zu versenden, sowie wie lange diese Daten aufbewahrt werden.
  • Eine öffentlich erreichbare Nutzungsbedingungen-Seite, die den Instagram- Automationsdienst als Teil des Angebots benennt.
  • Eine Löschmöglichkeit auf Anfrage (Data Deletion Request Callback URL oder gleichwertiger Prozess) — aktuell nicht separat fĂŒr Instagram implementiert; die bestehende „Trennen"-Funktion löscht den Access-Token, aber keine historischen Kommentar-/Nachrichtenereignisse oder Lead-DatensĂ€tze automatisch. Das ist eine offene LĂŒcke, die vor der tatsĂ€chlichen App-Review-Einreichung geschlossen werden muss.
  • Ein Demo-Video, das exakt die TestfĂ€lle oben zeigt (Verbindung → Automation einrichten → Kommentar → private DM → Inbox-Eintrag).

Offene manuelle Schritte (Meta-Konfiguration)

  1. Im Meta Developer Dashboard eine neue, separate App anlegen und das Produkt „Instagram" hinzufĂŒgen (nicht „Facebook Login for Business").
  2. Unter „Business Login" die Redirect-URI exakt eintragen: https://<domain>/api/public/oauth/instagram/callback.
  3. INSTAGRAM_APP_ID, INSTAGRAM_APP_SECRET (Werte des Instagram-Produkts, nicht einer Facebook-App), INSTAGRAM_OAUTH_STATE_SECRET (beliebiger zufĂ€lliger String, nur fĂŒr die CSRF-Signatur) und INSTAGRAM_VERIFY_TOKEN als Umgebungsvariablen hinterlegen (Vercel-Projekteinstellungen).
  4. App-Review fĂŒr instagram_business_manage_comments, instagram_business_manage_messages, instagram_business_content_publish einreichen (Datenschutz/ToS/Demo-Video wie oben, ĂŒblich 2–4 Wochen Bearbeitungszeit bei Meta).
  5. Bis zur Freigabe: App im Entwicklungsmodus mit bis zu 25 Test-Nutzern (unter den App-Rollen im Dashboard hinzugefĂŒgt) vollstĂ€ndig nutzbar — das reicht fĂŒr alle TestfĂ€lle oben. Die Webhook-Subscription passiert automatisch beim Verbinden (Schritt 5 im OAuth-Flow), keine manuelle Page-Konfiguration mehr nötig.