Kostenlos testen
Handbuch

Meta Ads (Facebook/Instagram)

Meta Marketing API deklarativ via api-Adapter — Kampagnen, AdSets, Ads, Budgets, Insights; für jeden Workspace, Geld ausgeben nur mit Freigabe.

Meta Ads laufen über die Marketing API (Graph API v22.0+). TaskMonkey bindet sie über den deklarativen api-Adapter mit Auth-Typ meta_long_lived_token ein — kein \Facebook\SDK, kein OAuth-Refresh. Die Tools liegen in config/tenants/_shared/tools/meta_ads/ und stehen damit jedem Workspace zur Verfügung — er braucht nur den apis.meta_ads-Block und seinen eigenen Token in der .env (seit 28.09.2026; vorher nur meco).

Was es gibt

Tool Art Geld? Freigabe
metaListCampaigns lesen: Kampagnen des Werbekontos – –
metaGetInsights lesen: CTR, CPC, Klicks, Conversions, Ausgaben je Kampagne/AdSet/Ad – –
metaSearchInterests lesen: Interessen fürs Targeting – –
metaCreateCampaign Kampagne anlegen, optional mit Kampagnenbudget (CBO) nein — immer PAUSED – (Budget ≤ Obergrenze)
metaCreateAdSet AdSet (Zielgruppe + Tagesbudget) nein — immer PAUSED – (Budget ≤ Obergrenze, DSA-Pflichtfelder)
metaCreateAdCreative Bild + Text + CTA + Ziel-URL – –
metaCreateAd Creative mit AdSet verknüpfen nein — immer PAUSED –
metaUpdateStatus PAUSED sofort (Notaus), ACTIVE = live, DELETED = endgültig ja ACTIVE/DELETED: Ja im Chat (ACTIVE in autonomen Workspaces ohne, bis zum Deckel)
metaUpdateBudget Tagesbudget von Kampagne (CBO) oder AdSet ja Erhöhen: Ja im Chat (autonome Workspaces: ohne, bis zum Deckel); Senken sofort
metaUpdateObject Roh-Schreibzugriff (status/daily_budget) ja nur über die zwei Hüllen oben (only_via)
metaDescribeObject, metaGetObject intern: Typ + Budgets eines Objekts (für die Prüfungen) – –

Regeln für Geld (gleiches Muster wie Google Ads):

  • Alles wird PAUSED angelegt. Live geht etwas nur über metaUpdateStatus(status=ACTIVE).
  • ACTIVE, DELETED und Budget-Erhöhungen laufen über confirmChatAction → ApprovalGate: der erste Aufruf liefert needs_confirmation + Vorschau + Ja/Nein-Buttons, erst ein klares Ja in der nächsten Nutzer-Nachricht plus derselbe Aufruf mit confirm=true schreibt. Ohne Menschen im Chat (test_tool/CLI, Scheduled Task) → Freigabe-Anfrage in TaskMonkey (status: pending_approval, freigeben unter /manage/approvals, siehe Google Ads → Freigabe-Anfragen); Public API, Inbox, Webhook → abgelehnt.
  • Obergrenze apis.meta_ads.max_daily_budget_eur (Default 30) pro Workspace, gilt pro Kampagne: Kampagnenbudget (CBO) bzw. Summe aller AdSet-Tagesbudgets. Darüber schreibt TaskMonkey nie — auch nicht mit Bestätigung. Beim Anlegen gilt sie je Budget, beim Aktivieren und Erhöhen für die ganze Kampagne danach.
  • Konto-Deckel der Plattform Platform.ads.max_account_daily_budget_eur (Default 50, pro Workspace in config/platform.php unter Platform.ads.workspaces.<code> anpassbar — nicht in der Workspace-Config, der Workspace kann ihn nicht selbst anheben): Summe der Tagesbudgets aller aktiven Kampagnen des Kontos inklusive der neuen bzw. geänderten. Geprüft beim Anlegen, Aktivieren und Erhöhen, vor der Rückfrage; nicht bestimmbar (z. B. Laufzeitbudgets) = abgelehnt.
  • Autonom ohne Rückfrage (29.09., nur per Plattform-Freigabe): Steht in config/platform.php für den Workspace Platform.ads.workspaces.<code>.autonomous = true plus autonomous_max_daily_eur (meco: true, 50), dann laufen metaUpdateStatus(ACTIVE) und metaUpdateBudget (Erhöhen) ohne confirmChatAction — solange alle aktiven Kampagnen des Kontos danach zusammen ≤ min(autonomous_max_daily_eur, max_account_daily_budget_eur) liegen. Darüber: Rückfrage wie oben bzw. Konto-Deckel. DELETED bleibt immer hinter der Rückfrage (nicht umkehrbar). Die Obergrenze je Kampagne (apis.meta_ads.max_daily_budget_eur) gilt weiter. Kern: App\Tooling\AdsAutonomy hinter dem internen Tool checkAdsAutonomy (only_via). Weitere Bedingungen:
    • Nur vertrauenswürdige Aufrufer (Prompt-Injection): test_tool/CLI, Scheduled Task des Workspaces, Chat eines angemeldeten Mitglieds (Assistant ohne public, Unified, Builder-Terminal, chat_<user>). Nie Inbox/Kanal-Chat, Public-Widget/-Assistant, Webhook/Hook, API (Tool-/Public-/External-API), Widget-Vorschau, Sub-Agent oder Headless-Runner ohne Nutzer, nie read-only.
    • Tageslimit Platform.ads.autonomous_max_actions_per_day (Default 50, pro Workspace überschreibbar) — danach wieder mit Rückfrage.
    • Protokoll: jede autonome Aktion schreibt eine eigene task_executions-Zeile (entry_type = 'ads_autonomy', tool_name = Tool, input_args = wer/was/vorher→nachher/Konto-Summe/Deckel); daraus zählt auch das Tageslimit.
    • Meldung danach per Mail (sendAdminEmail) an Platform.ads.workspaces.<code>.notify_emails, sonst an die admin_emails des Workspaces: „Kampagne X aktiviert, Konto jetzt Y €/Tag“.
    • Das Tool-Ergebnis trägt autonomous: true, account_daily_eur, audit_id.
    • Der Workspace kann sich die Freigabe nicht selbst geben (nicht in der Tenant-Config). Abschalten: Eintrag in config/platform.php entfernen bzw. autonomous => false.
  • Ist das Tagesbudget der Kampagne nicht bestimmbar (Laufzeitbudget, mehr als 200 AdSets), wird nicht aktiviert und nicht erhöht — dann im Werbeanzeigenmanager.
  • metaUpdateObject trägt only_via — der ToolRunner lehnt jeden Aufruf ab, der nicht direkt aus metaUpdateStatus oder metaUpdateBudget kommt (auch nicht über runTool oder einen Assistant, der das Tool freischaltet).
  • Pausieren und Budget senken gehen sofort, auch aus Scheduled Tasks (Notaus). Pausieren liest vorher nichts, damit es auch bei Lese-Fehlern greift.
  • Budgets rechnen in der Konto-Währung (Cent ↔ EUR); das Werbekonto sollte auf EUR stehen.

Setup-Reihenfolge (einmalig pro Tenant)

Der Setup läuft komplett im Meta Business Manager + Meta for Developers. Kein Code, aber Reihenfolge zählt — falsche Reihenfolge und du siehst "Keine Berechtigungen verfügbar" beim Token generieren.

1. Neue App im Business Manager anlegen

  • business.facebook.com/settings/apps → Hinzufügen → Neue App-ID erstellen
  • Name: z.B. TaskMonkey Ads
  • Anwendungsfall zunächst: Andere (Marketing-Use-Case wird gleich manuell dazu)
  • App-Typ: Business

Warum eine dedizierte Marketing-App und nicht die bestehende WhatsApp-App erweitern:

  • Saubere Trennung der Blast-Radien (Ads-App-Suspension bricht kein WhatsApp)
  • Token-Lifecycles unabhängig rotierbar
  • Meta-App-Reviews für Marketing sind strenger als für WhatsApp — Reviews nicht mischen

2. Anwendungsfall "Marketing" hinzufügen

Im App-Dashboard von TaskMonkey Ads:

  • Linke Sidebar: Anwendungsfälle → Anwendungsfall hinzufügen
  • Filter: Werbung und Monetarisierung
  • Diese zwei anhaken:
    • ✅ Werbeanzeigen mit Marketing API erstellen und verwalten (gibt ads_management, ads_read, business_management)
    • ✅ Anzeigenperformance-Daten mit Marketing API messen (gibt read_insights)
  • Alle anderen (Anzeigen-Leads, App Ads, MCP-Server, Audience Network, Catalog API) nicht anhaken — falscher Use-Case, macht das Token unnötig breit

3. App-Rolle für System-User setzen

Das ist der Schritt, den man leicht verpasst — ohne ihn geht Schritt 4 mit Fehlermeldung "Weise dem Systemnutzer eine App-Rolle zu".

  • In TaskMonkey Ads-Dashboard: App-Rollen in der Sidebar
  • System-Nutzer hinzufügen — deinen bestehenden System-User (z.B. Employee, den ihr für WhatsApp nutzt) auswählen
  • Rolle: Administrator

Alternativ: business.facebook.com/settings/system-users → System-User klicken → App zuweisen → TaskMonkey Ads wählen → Vollzugriff.

4. Token generieren

  • business.facebook.com/settings/system-users → System-User klicken → Token generieren
  • App: TaskMonkey Ads
  • Ablaufdatum: Nie
  • Berechtigungen (aus der Liste anhaken):
    • ✅ ads_management
    • ✅ ads_read
    • ✅ business_management
    • ✅ read_insights (falls in der Liste)
  • NICHT anhaken: ads_mcp_management, pages_manage_ads, catalog_management, instagram_basic, leads_retrieval
  • Stand 04.10.: Für Lead-Formulare und Reels braucht meco zusätzliche Rechte, aktuelle Liste in Reels veröffentlichen bzw. unten unter Lead-Formulare.
  • Token generieren → sofort kopieren (wird nur einmal angezeigt)

5. Werbekonto dem System-User zuweisen

  • business.facebook.com/settings/ad-accounts → dein Werbekonto klicken
  • Personen hinzufügen oder Zugriff hinzufügen → System-User Employee → Rolle Verwalten

Ohne diesen Schritt hat der Token zwar ads_management-Permission, aber keinen Zugriff auf ein konkretes Konto — Fehler bei jedem Call.

6. Werbekonto-ID kopieren

Auf derselben Seite (oben in der Detail-Ansicht) siehst du die Konto-ID im Format 2281091339403984 (16-stellige Zahl).

Für die Konfig brauchst du sie mit act_-Prefix: act_2281091339403984.

7. Konfiguration in TaskMonkey

In config/tenants/<tenant>/apis.php (Workspaces ohne Repo: gleicher Block, rein deklarativ):

'apis' => [
    'meta_ads' => [
        'base_url' => 'https://graph.facebook.com/v22.0/',
        'auth' => [
            'type' => 'meta_long_lived_token',
            'token' => '{{secret:META_ADS_ACCESS_TOKEN}}',
        ],
        'headers' => [
            'Accept' => 'application/json',
        ],
        // Nicht sensitiv — direkt hier oder als {{secret:META_ADS_ACT_ID}}:
        'act_id' => 'act_2281091339403984',
        // EU-DSA: Werbekunde + Zahler (Pflicht bei EU-Zielgruppen).
        'dsa_beneficiary' => 'Firma GmbH',
        // 'dsa_payor' => 'Firma GmbH',   // Default = dsa_beneficiary
        // Harte Obergrenze Tagesbudget pro Kampagne (Default 30).
        'max_daily_budget_eur' => 30,
    ],
],

In der .env des Workspaces (getrackt: tenants_untracked/<tenant>/.env, Server shared/tenants_untracked/<tenant>/.env):

META_ADS_ACCESS_TOKEN=EAA...  # der Token aus Schritt 4

Dann die Tools im Assistant freischalten (assistants.<name>.tools): die Lese-Tools, metaCreate*, metaUpdateStatus, metaUpdateBudget. metaUpdateObject, metaGetObject, metaDescribeObject nicht freischalten — die laufen nur als Sub-Tools.

8. Test

bin/cake test_tool --tenant <tenant> --tool metaListCampaigns --args='{"status":"ALL"}'

Erfolgreich = data-Array mit Kampagnen (leer, wenn noch keine angelegt). Fehler Object with ID '{{secret:...}}' does not exist = Placeholder wurde nicht aufgelöst → Deploy nach apis.php-Änderung nicht durch.

Long-Lived Token — Ablauf und Rotation

Der System-User-Token mit "Ablauf: Nie" läuft technisch nicht ab, aber:

  • Meta kann ihn invalidieren, wenn die App-Compliance sich ändert (z.B. Business-Verifizierung abläuft, App-Review-Status wechselt).
  • Rotation empfohlen: jährlich, oder wenn ein Ex-Mitarbeiter im System-User-Admin war.
  • Bei Rotation: neuen Token generieren (Schritt 4), META_ADS_ACCESS_TOKEN in .env updaten, kein Redeploy nötig (Cake liest .env bei jedem Request).

Monitoring: Der Token verrät sich beim Ausfallen erst bei Aufruf — kein Vorwarn-Signal. Empfehlung: einmal pro Woche metaListCampaigns als Health-Check via Cron, bei 401/403 sofort neue Token-Generation.

Auth-Layer im ToolRunner

Der Adapter nutzt den Auth-Type meta_long_lived_token im Auth-Block von src/Tooling/ToolRunner.php. Ablauf pro Call:

  1. base_url muss https://*.facebook.com sein — sonst gibt der Runner keinen Token heraus (Meta-Auth nicht erlaubt). Eine Workspace-Config kann den Token so nicht an einen fremden Host schicken lassen.
  2. ToolRunner liest apis.<name>.auth.token
  3. expandSecrets löst {{secret:META_ADS_ACCESS_TOKEN}} gegen die Workspace-.env auf
  4. Token wird als Query-Param access_token an jeden Meta-Call gehängt (nicht als Bearer-Header — Meta will es so)

IDs aus dem Chat (object_id, act_id) prüfen die Tools auf reine Ziffern bzw. act_<Ziffern>, bevor sie im Pfad landen.

Auth-Layer ist generisch: jede Meta-API (Ads, WhatsApp Business, Instagram Business) funktioniert gleich, nur mit anderem Token und anderen Endpoints.

Business-Manager-Verifizierung

Werbekonten in einem verifizierten Business haben höhere Limits (Ausgaben, gleichzeitige aktive Kampagnen). Verifizierung dauert 1-5 Werktage, braucht Handelsregister-Auszug oder Steuer-ID.

Nicht kritisch für den Start — TaskMonkey Ads funktionieren auch mit unverifiziertem Business, nur mit engeren Ausgaben-Limits (~€10.000 lifetime spend cap).

Häufige Fehler

Fehler Ursache Fix
Object with ID '{{secret:...}}' does not exist Placeholder nicht aufgelöst .env prüfen, Deploy neu
Unsupported get request... missing permissions System-User hat Werbekonto nicht zugewiesen Schritt 5 nachziehen
(#100) Invalid parameter Meist Budget in EUR statt Cent body_transform prüft (in Tools schon drin)
Application does not have the capability Berechtigung fehlt im Token Schritt 4 wiederholen mit vollständiger Auswahl
(#803) Some of the aliases you requested do not exist act_id falsch act_<Ziffern> in apis.php (reine Ziffern ergänzen die Tools selbst)
apis.meta_ads.act_id fehlt oder ist ungueltig act_id leer Werbekonto-ID eintragen
dsa_beneficiary fehlt EU-DSA-Pflichtfeld apis.meta_ads.dsa_beneficiary setzen oder als Argument mitgeben
… über der Obergrenze … Budget > max_daily_budget_eur Budget senken oder Obergrenze bewusst anheben (Config-Änderung, apis ist in ApprovalGate sensitiv)
error_code: only_via metaUpdateObject direkt aufgerufen über metaUpdateStatus / metaUpdateBudget
(#2635) You are calling a deprecated version Graph-API-Version zu alt base_url auf aktuelle Version updaten

Lead-Formulare (Instant Forms) — meco-Workspace

Seit 02.10.2026. Jeder Lead aus einem Facebook-/Instagram-Lead-Formular landet innerhalb weniger Minuten in TaskMonkey und wird Marcus sofort gemeldet.

Teil Wo
Abruf (Polling, Webhooks in der EU blockiert) pollMetaLeads, scheduled.php → poll_meta_leads (läuft in jedem meco-Cron-Tick zuerst, Cron alle 5 min)
Graph-Calls (deklarativ, read-only) metaListLeadForms (/{page_id}/leadgen_forms), metaGetFormLeads (/{form_id}/leads)
Auth apis.meta_leads, Auth-Type meta_page_token: Runner leitet aus META_ADS_ACCESS_TOKEN den Seiten-Token ab, nur für page_ids
Ablage Supabase leads: campaign = source = meta_lead_form, outreach_status = warm_inbound, Dedupe (source, external_id); Schema db/supabase/meco_2026-10-02_meta_leads.sql
Meldung notifyMetaLead (Core MetaLeadAlertTools): Mail immer, WhatsApp über die Plattform-Nummer (24h-Fenster oder Template), Zeile Anrufen: tel:+49… = Anruf-Link in der Mail. Empfänger Onboarding.alert.meta_lead_recipients (leer = recipients)
Auto-Antwort an den Lead sendMetaLeadAutoReply, apis.meta_leads.auto_reply.enabled — Standard aus
Aufbewahrung purgeMetaLeads täglich, retention_days 180 (= Datenschutzerklärung Abschnitt 19), outreach_status = 'kunde' bleibt

Kaltmail-Strecke: meta_lead_form ist keine lead_campaigns-Kampagne, generateOutreach überspringt die Quelle zusätzlich. Warme Leads bekommen keine Outreach-Mail.

Klickliste Marcus (Stand 02.10.)

Ist-Stand Token (debug_token, 02.10.): Systemnutzer „Taskmonkey“ (122112517077482065), App „TaskMonkey Ads“ (1047000088377962), Rechte nur ads_management, ads_read, business_management. Keine Seite zugewiesen (me/accounts leer), /{page}/leadgen_forms → „Requires pages_manage_ads“.

  1. developers.facebook.com → App „TaskMonkey Ads“ → Anwendungsfälle → Hinzufügen „Leads aus Werbeanzeigen erfassen und verwalten“ (Lead Ads). Darin leads_retrieval, pages_manage_ads, pages_read_engagement, pages_show_list hinzufügen. Standard-Zugriff reicht (nur eigene Seite, kein App Review).
  2. business.facebook.com → Einstellungen → Nutzer → Systemnutzer → „Taskmonkey“ → Assets zuweisen → Seiten → meco-Seite (108629875903438), später zusätzlich die neue TaskMonkey-Seite: Vollzugriff (mindestens „Werbeanzeigen“ + „Leads“).
  3. Einstellungen → Integrationen → Leads-Zugriff → Seite wählen → Systemnutzer „Taskmonkey“ Zugriff geben (nur nötig, wenn dort schon Einschränkungen gesetzt sind; schadet nicht).
  4. Systemnutzer „Taskmonkey“ → Token generieren → App „TaskMonkey Ads“, Ablauf „Nie“, Häkchen: ads_management, ads_read, business_management, leads_retrieval, pages_show_list, pages_read_engagement, pages_manage_ads → Token an den Orchestrator (ersetzt META_ADS_ACCESS_TOKEN in tenants_untracked/meco/.env, lokal und auf Prod).
  5. Lead-Ads-Bedingungen der Seite akzeptieren: https://www.facebook.com/ads/leadgen/tos (einmal pro Seite, sonst lässt Meta keine Lead-Anzeige zu).
  6. Im Formular als Datenschutz-Link: https://taskmonkey.de/datenschutz#lead-formulare.

Danach (Orchestrator): Deploy, Supabase-Migration ausführen, dann bin/cake test_tool -t meco --tool pollMetaLeads --args='{"dry_run":true}' und einen Test-Lead über das Lead Ads Testing Tool (developers.facebook.com/tools/lead-ads-testing) anlegen. Neue Seite: ID in apis.meta_leads.page_ids ergänzen.

Fehlen Rechte, schreibt pollMetaLeads höchstens eine Hinweis-Mail pro Tag an die meco-Admins und liefert error_code: meta_permissions_missing.

Verwandt

  • google-ads.md — Google Ads, gleiches Freigabe-Muster
  • google-workspace.md — der Auth-Layer für Google APIs (google_service_account) — analoges Muster
  • oauth-flow.md — allgemeiner OAuth-Flow (falls du eine App für User-Delegation baust, nicht System-User)
Zuletzt aktualisiert: 2026-09-28