Zum Hauptinhalt springen

AgencyOS: CockpitOS anbinden (Magic Link & Integration API)

Diese Seite richtet sich an Entwickler:innen von AgencyOS (Team-Produkt unter team.cockpit-os.de). Sie beschreibt, was AgencyOS implementieren muss, um eine Vertrauensstellung mit dem CockpitOS-Dashboard herzustellen und Center sowie Inhalte über die REST-API anzusprechen. Dabei gibt es zwei Geltungsbereiche für den API-Key: eine Organisation (klassisch) oder nutzerweit (alle Center gemäß den Cockpit-Rechten der anbindenden Person, organisationenübergreifend inkl. zugewiesener Center ohne Organisation).

Produkt: AgencyOS · Dashboard-Implementierung (Monorepo): u. a. apps/dashboard/src/app/api/agencyos/…, apps/dashboard/src/app/agencyos/connect/page.tsx

Schlüssel ohne Magic Link (im Dashboard): Eingeloggte Nutzer:innen mit passender Berechtigung können unter Einstellungen → Integrationen eine AgencyOS-Integration anlegen bzw. den API-Key rotieren (GET/POST /api/agencyos/integrations, POST/rotate) — sinnvoll z. B. für lokale Tools (MCP, Skripte). Vollständiger Key wie gewohnt einmalig in der Antwort, nicht dauerhaft in der UI.

Remote-MCP (HTTP, Monorepo): Paket packages/mcp-cockpit-remote stellt dieselben MCP-Tools per Streamable HTTP bereit (für Organisationen mit Claude-„Remote MCP“-URL). Nicht Teil der öffentlichen Dashboard-API; Betrieb mit eigenem Secret COCKPIT_MCP_HTTP_BEARER und COCKPIT_AGENCYOS_API_KEY im Server-Env. Siehe Paket-README.

Basis-URL

Ersetzen Sie {DASHBOARD_ORIGIN} durch die öffentliche URL Ihrer Dashboard-Instanz (z. B. https://dashboard.cockpit-os.de). Lokal oft http://localhost:3000. Die Variable NEXTAUTH_URL im Dashboard bestimmt die in Magic-Link-Antworten eingebettete Basis-URL.

Überblick: Was AgencyOS tun muss

  1. Magic Link anfordern (serverseitig, ohne Nutzer-Session im Cockpit): POST /api/agencyos/magic-link mit integrationName und optional returnUrl.
  2. Nutzer:in zum Cockpit leiten: Response enthält data.magicLink (Pfad /agencyos/connect?token=…). Dort meldet sich eine berechtigte Person an und wählt entweder eine Organisation oder „Alle Center mit meinen Zugriffsrechten“ (nutzerweiter Key).
  3. API-Key per Polling holen: Solange status === "pending", regelmäßig GET /api/agencyos/magic-link?token=… aufrufen. Sobald status === "completed", liefert die Antwort data.apiKey (Präfix typisch sk_agencyos_) sowie data.accessScope ("organization" oder "user") und data.organizationId (UUID oder null bei nutzerweitem Key).
  4. Speichern: API-Key sicher in AgencyOS hinterlegen (Geheimnis, nicht in Logs/URLs).
  5. API nutzen: Alle folgenden Aufrufe mit Authorization: Bearer <apiKey> gegen /api/agencyos/v1/….

Sicherheit und Redirect

  • Den API-Key niemals in die Redirect-URL legen (kein Query-Parameter mit Secret). Die Übernahme erfolgt ausschließlich über das Polling des Magic-Link-Status.
  • Wenn ihr eine returnUrl (z. B. zurück nach https://team.cockpit-os.de/...) mitsendet, kann das Cockpit nach erfolgreichem Abschluss dorthin weiterleiten und setzt u. a. agencyos=connected sowie cockpit_agency=connectedohne Key. Den Key nur aus der GET-Antwort lesen, wenn status === "completed".

Unterschied zu WordPress

AspektWordPress-PluginAgencyOS
Geltungsbereichein Center pro Website-KeyOrganisation: alle Center dieser Org · Nutzer: alle Center, auf die die anbindende Person in Cockpit Zugriff hat (mehrere Orgs + ohne Org)
Verbindungs-UI/wordpress/connect/agencyos/connect
Content-PushPOST /api/wordpress/push-content (Key = Website)POST /api/agencyos/v1/content/push mit centerId im JSON
Doku Push-BodyWordPress Push-ContentGleiche Entity-Arrays (shops, events, …); siehe unten

POST {DASHBOARD_ORIGIN}/api/agencyos/magic-link

  • Auth: keine (öffentlich wie beim WordPress-Magic-Link).
  • CORS: Access-Control-Allow-Origin: *, OPTIONS unterstützt.

Body (JSON):

FeldTypPflichtBeschreibung
integrationNamestringjaAnzeigename der Integration in Cockpit (z. B. "AgencyOS Produktion")
returnUrlstringneinhttp:// oder https://; nach Erfolg optional Redirect aus dem Browser

Erfolg (200):

{
"success": true,
"data": {
"magicLink": "https://…/agencyos/connect?token=mla_…",
"token": "mla_…",
"expiresAt": "2026-04-01T12:00:00.000Z"
},
"message": "Magic Link erstellt"
}

Hinweis: Token-Gültigkeit 15 Minuten ab Erstellung (sofern nicht vorher abgeschlossen).


GET {DASHBOARD_ORIGIN}/api/agencyos/magic-link?token=<token>

  • Auth: keine.

Solange die Verbindung aussteht, ist data.status typischerweise "pending". Nach Abschluss im Browser:

  • data.status === "completed"
  • data.apiKey gesetzt
  • data.integrationId gesetzt
  • data.accessScope: "organization" oder "user"
  • data.organizationId: UUID der verbundenen Organisation oder null, wenn accessScope === "user"

AgencyOS-Implementierung: Nicht von einem festen organizationId im Key ausgehen. Bei accessScope === "user" ist organizationId absichtlich null; die erlaubten Center ergeben sich aus den Cockpit-Rechten des Nutzers (siehe GET /v1/centers).

Fehler: u. a. 404 ungültiger Token, 410 abgelaufen (bei noch pending).


3. Verbindung im Browser abschließen (nicht von AgencyOS-Server)

Dieser Schritt läuft im Cockpit mit NextAuth-Session; AgencyOS ruft ihn normalerweise nicht per Server-to-Server auf.

  • UI: GET /agencyos/connect?token=…
  • Organisationen laden (Session): GET /api/agencyos/organizations (für die klassische Variante; nutzerweite Option ist auch ohne Einträge in der Liste möglich)
  • Abschluss: POST /api/agencyos/magic-link/complete mit JSON:
    • Organisations-Key: { "token": "…", "organizationId": "<uuid>" }
    • Nutzer-Key: { "token": "…", "accessScope": "user" } (kein organizationId nötig)

Die Response enthält u. a. apiKey, accessScope, organizationId (nullable) für die sofortige Anzeige im Browser – für AgencyOS ist weiterhin das Polling die Quelle der Wahrheit, damit der Backend-Prozess den Key zuverlässig erhält.


4. AgencyOS API v1 (Bearer API-Key)

Alle Endpunkte unter /api/agencyos/v1/ erwarten:

Authorization: Bearer <apiKey>

apiKey ist der aus Schritt 2 übernommene AgencyOS-Integration-Key (sk_agencyos_…) – entweder an eine Organisation oder nutzerweit gebunden (accessScope aus der Polling-Antwort).
CORS: Access-Control-Allow-Origin: * (u. a. für GET, POST, PATCH, OPTIONS).

Medien-Upload zu Bunny (Bilder & Videos)

POST {DASHBOARD_ORIGIN}/api/agencyos/v1/media/upload

  • Auth: Authorization: Bearer <apiKey>
  • JSON (Variante A): { "url": "https://…", "folder?": "agencyos/uploads" } — Ressource wird geladen und nach BunnyCDN kopiert (Bild oder Video).
  • JSON (Variante B): { "base64": "…", "mimeType?": "video/mp4", "filename?": "…", "folder?": "…" } — optional mit data:mime;base64,-Präfix.
  • Raw Body: Content-Type: image/* oder video/* oder application/octet-stream; Query ?folder=&filename= — Rohbytes direkt nach Bunny.
  • Größenlimits: in etwa 10 MB für typische Bilder, 100 MB für Video (Implementierung in route.ts).
  • Antwort: { "success": true, "bunnyUrl": "https://…b-cdn.net/…" } (bereits Bunny-URLs werden unverändert bestätigt).

MCP: Paket @mall-os/mcp-cockpit-os, Tool cockpit_agencyos_upload_media — Parameter url oder base64 (plus optional mimeType, filename, folder).

Mediathek listen: GET {DASHBOARD_ORIGIN}/api/agencyos/v1/media?centerId=<uuid> — Filter type, entityType, q, includeShared, limit, offset. MCP: cockpit_agencyos_list_media.

UI (Redaktion): Video wie Bilder über FileUploadPOST /api/upload (Multipart, Bunny-Pfad z. B. centers/{centerId}/{entityType}/video/…) und „Aus Mediathek“; nicht zu verwechseln mit dieser AgencyOS-JSON-Route.

4.1 Shopping Center auflisten

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/centers

Response (200): { "success": true, "data": [ { "id", "name", "slug", "address", "city", "postalCode", "country", "status", "websiteEnabled", "organizationId", "agencyIntegrationId", "updatedAt" }, … ] }
Max. 500 Einträge, sortiert nach Name.

Bei accessScope === "organization" (Standard):

  • alle Center mit organizationId = Organisation des API-Keys, und
  • Center ohne Organisation (organizationId: null), die von genau dieser Agency-Integration angelegt wurden (agencyIntegrationId = Integration des Keys).

Bei accessScope === "user":

  • alle Shopping Center, auf die der anbindende Cockpit-Nutzer Zugriff hat (z. B. über UserCenterAssignment, Heimat-Organisation, Super-Rollen – wie in Cockpit definiert), und
  • dieselben integrationsgebundenen „Waisen“-Center wie oben (ohne Organisation, aber agencyIntegrationId dieser Integration).

So erscheinen „freie“ Center nur im Kontext der eigenen Integration, ohne fremde ungebundene Center zu leaken.

4.2 Shopping Center anlegen

POST {DASHBOARD_ORIGIN}/api/agencyos/v1/centers

Body (JSON) – Pflichtfelder:

FeldTypBeschreibung
namestring
addressstring
citystring
postalCodestring

Optional: country (Default "DE"), slug (sonst automatisch aus Name, global eindeutig), description, phone, email, website, status (Default "active").

Organisation (wie im Cockpit):

FeldTypBeschreibung
Standard (Organisations-Key)Ohne die folgenden Felder wird organizationId auf die Organisation des API-Keys gesetzt.
Standard (Nutzer-Key)Nicht ohne Ziel-Org: Es muss entweder withoutOrganization/noOrganization/organizationId: null oder eine explizite organizationId (UUID) gesendet werden, für die der anbindende Nutzer in Cockpit berechtigt ist; sonst 400/403.
withoutOrganization / noOrganizationboolean trueCenter wird ohne Organisation angelegt (organizationId: null), bleibt aber dieser Integration zugeordnet (intern agencyIntegrationId), damit sie es in GET/Push weiter nutzen kann.
organizationIdnullGleiche Bedeutung wie withoutOrganization: true.
organizationIdstring (UUID)Organisations-Key: nur erlaubt, wenn der Wert exakt der Organisation des Keys entspricht; sonst 403. Nutzer-Key: erlaubt, wenn der Nutzer diese Organisation verknüpfen darf (wie im Cockpit); sonst 403.

Erfolg: HTTP 201, data enthält u. a. organizationId und agencyIntegrationId.

Fehler: 409 wenn slug bereits vergeben.

4.3 Einzelnes Center lesen / aktualisieren

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}
PATCH {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}

Zugriff, wenn das Center für diesen Key erlaubt ist (Organisations-Key: gleiche Organisation oder integrationsgebundenes Waisen-Center; Nutzer-Key: gemäß Nutzerrechten oder integrationsgebundenes Waisen-Center); sonst 404.

PATCH: nur gesendete Felder werden geändert. Erlaubt u. a. name, address, city, postalCode, country, phone, email, website, description, openingHours, status, slug, websiteEnabled. Pflichtfelder dürfen nicht auf null gesetzt werden.

Später an eine Organisation hängen: assignToKeyedOrganization, assignToOrganization oder linkToKeyedOrganization mit true – nur wenn das Center aktuell ohne Organisation ist und von dieser Integration stammt.

  • Organisations-Key: Es wird die Organisation des Keys verbunden (agencyIntegrationId wird entfernt).
  • Nutzer-Key: Zusätzlich attachOrganizationId (UUID) im JSON-Pflicht – Zielorganisation, an die gehängt werden soll; nur wenn der anbindende Nutzer dafür in Cockpit berechtigt ist; sonst 403. (agencyIntegrationId wird entfernt.)

Website-Konfiguration (GET + partial PUT)

Lesen: GET {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/website-config
Liefert designConfig, seoConfig, contentConfig, legalConfig, parkingConfig, analyticsConfig, centerplanConfig, pagesConfig, templateContent (Template-Reiter).

Schreiben: PUT {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/website-config
Partial-Update; templateContent wird deep-merged.

Reiter-Index (Schema): GET {DASHBOARD_ORIGIN}/api/agencyos/v1/website-config-schema?websiteTemplate=ilg
oder ?centerId={uuid} — Dashboard-Reiter mit Speicherort, fields[] (exakte JSON-Pfade), v0PublicRead (öffentliche Live-Site) und MCP-Hinweisen für alle Website-Templates.

v0 / Claude Live-Website (LESEN, ohne Auth):
GET …/api/centers/{centerId}/public-visitor-surfacedata.templatePublicContent (Template-Reiter: Hero, Footer, …) + data.apiHints.pageContentGet / homepageTilesGet.
Nicht GET …/website-config im Browser — 401. Schreiben bleibt MCP: cockpit_agencyos_*. Antwort enthält v0Integration (Lesen-vs-Schreiben-Guide).

Page Content (Hero, SEO, customContent): POST …/page-content — beim Update werden nur mitgesendete Scalar-Felder geändert; customContent wird deep-gemerged (z. B. customContent.ilg.anfahrtBoxes ohne andere Seitenfelder zu löschen).

Empfohlener MCP-Workflow (ILG/RGW):

  1. cockpit_agencyos_website_config_schema (mit websiteTemplate oder centerId)
  2. Reiter aus tabs[] wählen → fields[] / templateContentPath / customContentPath lesen
  3. Bestehende Werte per GET (get_center_website_config / page_content)
  4. Partial PUT/upsert nur mit geänderten Keys
  5. Revalidate (automatisch in API-Response, sofern konfiguriert)

MCP:

ToolFunktion
cockpit_agencyos_get_center_website_configVolle Config lesen
cockpit_agencyos_update_center_website_configPartial Update
cockpit_agencyos_website_config_schemaReiter + Feldpfade pro Template
cockpit_mcp_discover_toolsTool-Index wenn Claude tool_search scheitert

contentConfig.specialDays (Sonderöffnungszeiten): JSON-Array oder dasselbe als JSON-String. Pro Eintrag z. B. { "date": "2026-12-24", "label": "Heiligabend", "hours": { "open": "10:00", "close": "14:00" } } — geschlossen mit "hours": null. Optional "image" (URL).

Die Route schreibt in ShoppingCenter.specialDays und spiegelt bei strukturiertem openingHours (mit regularHours) zusätzlich openingHours.specialDays — analog zum Speichern im Cockpit „Center bearbeiten“.

4.3a Center-Kontext für KI lesen (Shops / Services)

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/context

Zugriff wie GET /v1/centers/{centerId} (Bearer-Key, sonst 404).

Liefert gebündelte Lesedaten für AgencyOS/KI, ohne die öffentliche Website-API zu nutzen:

QueryDefaultMaxBeschreibung
includecenter,shopsKomma-getrennt: center, shops, services, news, events, offers, categories, chains, floors_summary
shopLimit5002000Max. Anzahl kombinierter Einträge (Einzelshops + Filialen), sortiert nach Name
serviceLimit200500Nur wenn services in include
newsLimit80200Nur wenn news in include
eventsLimit80200Nur wenn events in include
offersLimit80200Nur wenn offers in include

Filter: Shops und Filialen wie GET …/centers/{centerId}/shops?publicWebsite=true&status=Aktiv (inkl. Veröffentlichungsfenster). Services wie websiteServicePublicFilter (Center-Website-SSR).

News / Events / Angebote: Alle Datensätze dieses Centers (jeder Status, inkl. Entwurf), sortiert nach updatedAt absteigend — für Kontext, Dedupe und Abgleich mit AgencyOS. Keine Volltexte; pro Zeile u. a. id, title, slug, status, Daten, source, sowie agencyosPushId und wordpressPushId aus metadata, falls gesetzt.

categories: Wie getWebsiteShopCategories (Center + Global, minShopCount: 0, Status Aktiv für Zählung) — nur Referenzen zur Token-Optimierung: je Eintrag id, name, slug (keine Zähler/Icons/Farben im Kontext).

chains: Alle ShopChain, die über Shops oder Filialen (ShopLocation) an dieses Center angebunden sind (max. 500, nach Name) — nur id, name, slug.

floors_summary: Für jede aktive Etage ein kompaktes Objekt — u. a. floorId, name, floorNumber, mapSvgChars (Zeichenzahl von mapSvg ohne Auslieferung des Strings), suggestsHybridSvg (Heuristik: typische Hybrid-Marker im Markup), hasMapImageUrl, hasShopViewBoxes, shopViewBoxesKeyCount, mapLocationActiveCount. Für Svg-Markup, mapLocations und Routenplanung weiter die öffentliche Route GET /api/wayfinding/floors?centerId= oder das MCP-Tool cockpit_public_wayfinding_floors.

Response (200): { success: true, data: { center?, shops?, services?, news?, events?, offers?, categories?, chains?, floors_summary? }, meta: { …limits, counts } }
data.shops[]: kompakte Objekte u. a. id, name, category, slug, floor, location, status, isShopLocation, chain, logo, coverImage, displayId.

4.3b Wartung: Duplikate, Ketten-Merge, Domains, Centerplan

Zusätzliche AgencyOS v1-Routen (Bearer, Center-Zugriff wie bei anderen /v1/centers/{centerId}-Endpunkten):

RouteMethodeZweck
…/centers/{centerId}/shop-duplicatesGETPotenzielle Shop-Duplikate im Center (Namens-Ähnlichkeit, Query threshold 0.5–1, optional includeArchived). Antwort: Gruppen mit suggestedKeepId und archiveCandidateIds.
…/chains/duplicatesGETPotenzielle ShopChain-Duplikate global (Query threshold, Default 0.8).
…/chains/mergePOSTKetten zusammenführen (Quelle → Ziel): Filialen/Einzelshops umhängen, Quell-Kette löschen. Body: sourceChainId + targetChainId, optional dryRun, oder Bulk pairs[] (max. 25).
…/centers/{centerId}/verify-domainsPOSTDNS/HTTPS für Custom Domains prüfen und domainStatus / sslStatus in der DB aktualisieren.
…/centers/{centerId}/wayfinding/floorsGETKompakte Etagen-Liste (IDs, Metriken, Zähler) ohne mapSvg-Body — für MCP/KI; volles SVG weiter GET /api/wayfinding/floors.
…/centers/{centerId}/map-locationsGET, POSTMapLocations listen (floorId, activeOnly) bzw. anlegen.
…/centers/{centerId}/map-locations/assignPOSTShop/Service an SVG-Fläche zuordnen (floorId, svgId, upsert).
…/map-locations/{locationId}PATCHMapLocation aktualisieren (Zuordnung, Geo, aktiv/inaktiv).

Hilfslogik: apps/dashboard/src/lib/integration/ (find-shop-duplicates, find-chain-duplicates, merge-shop-chains, shop-name-similarity, agency-map-location). Duplikat-Archivierung kann über POST …/content/bulk-archive mit contentType: "shop" erfolgen.

4.4 Inhalte ins Center pushen

POST {DASHBOARD_ORIGIN}/api/agencyos/v1/content/push

Gleiches Konzept wie WordPress Push-Content: Body mit optionalen Arrays shops, events, news, offers, services.
Zusätzlich Pflicht:

FeldTypBeschreibung
centerIdstringUUID des Centers; zulässig unter denselben Regeln wie GET /v1/centers/{centerId} (Organisations-Key vs. Nutzer-Key inkl. Waisen dieser Integration)

Hinweise:

  • Es gibt keine WordPress-pages-Speicherung in WordPressWebsite; Fokus liegt auf den Entitäts-Arrays.
  • In Cockpit werden betroffene Inhalte mit source agencyos markiert (analog wordpress beim WP-Endpunkt).

Idempotenz (Events, News, Angebote, Services — wie Shops)

Für events[], news[], offers[], services[] gilt dieselbe externe Referenz wie bei Shops:

  • agencyosId, externalId, clientReference oder redaktionsReferenz, alternativ bei AgencyOS-Push ein id, das keine Cockpit-UUID und kein wp_* ist.
  • Cockpit speichert den Wert unter metadata.agencyosPushId und findet beim nächsten Push dieselbe Zeile zum Update (kein Duplikat).
  • Reihenfolge der Zuordnung: Cockpit-UUIDagencyosPushIdWordPress-wordpressPushIdslug → (nur WordPress) Legacy nach Titel.

Zusätzlich: publishDate bei News akzeptiert auch Alias publishedAt oder date.

Shops: Ketten & KI-Redaktion

Redakteure arbeiten in AgencyOS oft per natürlicher Sprache mit einer KI. Die API ist so ausgelegt, dass die KI nicht zwingend Cockpit-UUIDs kennen muss:

ZielEmpfohlene Felder im shops[]-Eintrag
Dieselbe Marke nicht hundertmal anlegenPro logischem Shop eine stabile Referenz: agencyosId oder externalId (oder bei source-Push id als freier String, keine UUID) – wird im Cockpit unter metadata.agencyosPushId gespeichert und beim nächsten Push zum Update verwendet. Zusätzlich slug pro Center, falls vorhanden.
„Deichmann ist eine Kette“kette, marke, chainName, brandName oder shopChainName mit dem Markennamen → Cockpit verbindet den Shop mit dieser ShopChain (existiert sie nicht, wird sie angelegt; nur neue Kettendaten, keine Löschungen).
Bereits bekannte Kette (UUID aus Cockpit)shopChainId oder chainId setzen.
Nur Einzelshop ohne KettestandaloneShop / einzelshop / clearShopChain: true oder modus: "einzel" / "standalone".

Fachlicher Hinweis: Im Cockpit gibt es zusätzlich Filialen als ShopLocation (Kette + Center). Der Push landet zunächst auf dem Shop inkl. shopChainId-Verknüpfung; eine vollständige Filialen-Spiegelung ist davon getrennt und kann bei Bedarf später ergänzt werden.

Ausführliche Feldliste (inkl. WordPress): WordPress Push-Content – shops.

Response: analog WordPress: success, data.summary, optional data.errors.

4.4a Push-Vorschau (keine DB-Änderung)

POST {DASHBOARD_ORIGIN}/api/agencyos/v1/content/push/preview

  • Gleicher JSON-Body wie POST …/content/push (inkl. centerId), gleicher Bearer-API-Key und Center-Zugriff.
  • Keine create/update-Schreiboperationen in der Datenbank — geeignet für Freigabe-Workflows (z. B. AgencyOS zeigt die geplanten Änderungen, danach echter Push).
  • Response (200): success, message, data.previewPlan (Liste geplanter Schritte mit entity, action create/update, match bei Zuordnung, existingId, planned, optional notes), data.summary, optional data.errors (Validierungs-/Ketten-Fehler wie beim echten Push).
  • Shop-Ketten: Würde eine neue ShopChain angelegt (namensbasierte Auflösung), erscheint das in planned.shopChain als would_create_chain ohne die Kette anzulegen; Hinweis ggf. in notes.
  • GET …/preview: wird nicht unterstützt (405) — JSON-Payload gehört in den POST-Body.

Content-Entwürfe (Workflow) — Lesen, Freigabe, Kundenkontakt

Dieselbe ContentDraft-Logik wie unter Workflow & Freigaben im Dashboard, per AgencyOS-Key:

MethodePfadKurzbeschreibung
GET/api/agencyos/v1/draftsListe; Query wie bisher (status, contentType, centerId, campaignLabel, limit). Neu: includeData=1 — pro Eintrag geparstes data (Längen serverseitig begrenzt), u. a. customerCommunication, dispatchItemId, Inhaltsfelder.
GET/api/agencyos/v1/drafts/{draftId}Ein Entwurf; optional ?includeData=1.
PUT/api/agencyos/v1/drafts/{draftId}approve / reject (unverändert).
POST/api/agencyos/v1/drafts/{draftId}/customer-touchpoint-suggestionBody: { "scenario": "workflow_approve" | "workflow_reject" | "workflow_pending", "rejectionReason"?: string }gleiche KI-Vorschläge wie im Dashboard (Betreff, E-Mail-Entwurf, interne Hinweise). Nutzt die globale Cockpit-KI-Konfiguration.

MCP/Claude: cockpit_agencyos_list_drafts (optional includeData: true), cockpit_agencyos_get_draft, cockpit_agencyos_draft_customer_touchpoint, cockpit_agencyos_update_draft. Pro Entwurf: createdBy, createdByName, source, createdAt.


Audit-Log (Provenance)

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/audit-logs

Bearer-API-Key; Center-Zugriff wie bei anderen v1-Routen.

QueryBeschreibung
centerIdLetzte Änderungen im Center (Tagesüberblick) — oder
entityType + entityIdHistorie eines Eintrags (offer, news, shop, event, service, job, center, shop-location)
limitDefault 50, max 100
includeValues1 / trueoldValues / newValues mitliefern (Feldänderungen)

Response (200): success, centerId, count, data[] mit u. a. action (CREATE/UPDATE/DELETE), entityType, entityId, userName, userId, timestamp, optional metadata.

MCP: cockpit_agencyos_audit_logs — z. B. „Wer hat dieses Angebot zuletzt bearbeitet?“ (entityType + entityId aus search_content). Mit summary: true / Query summary=1: Rangliste byUser (ideal für „wer war am aktivsten?“). Ab Deploy: auch MCP-Push, Uploads und Center-Anlage via AgencyOS werden protokolliert (metadata.integrationName, channel: agencyos).


Center-Team (Zugriff & Rollen)

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/team

Bearer-API-Key; Center-Zugriff wie bei anderen v1-Routen.

Response (200): center, count, data[] mit zugewiesenen Nutzer:innen (user.name, user.email), centerRole, Content-/Shop-Berechtigungen, assignedAt.

MCP: cockpit_agencyos_center_team — z. B. „Wie viele Personen haben Zugriff auf Center X?“


Website-Cache (Revalidate)

Nach POST …/content/push (Direkt-Push, keine Drafts) invalidiert das Dashboard automatisch den Next.js-Cache auf allen in CENTER_WEBSITE_URLS / CENTER_WEBSITE_URL eingetragenen Instanzen — wenn REVALIDATION_SECRET gesetzt ist. Die Push-Antwort kann data.revalidation enthalten (instances[] pro URL).

Manuell: POST {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/revalidate (Bearer-Key, Body optional { "fullRevalidation": true }).

MCP: cockpit_agencyos_revalidate_website

v0/Vercel: Live-Seite braucht app/api/revalidate/route.ts, REVALIDATION_SECRET auf Vercel (identisch zum Dashboard) und websitePublicUrl im Center (automatisch via Deploy-Registrierung oder manuell im Dashboard). Legacy: globale CENTER_WEBSITE_URLS. Ohne Revalidate-Secret: Daten in Cockpit aktuell, Vercel kann ISR-Cache zeigen.


Frontend-Deploy registrieren (v0 → Cockpit)

POST {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/frontend-deployments/register

Bearer-API-Key; Center-Zugriff wie bei anderen v1-Routen.

Body (JSON):

FeldTypPflichtBeschreibung
channelstringjawebsite | signage | companion
originstringjaDeploy-Origin ohne Pfad, z. B. https://xyz.vercel.app
sourcestringneinz. B. vercel, v0, mcp

Response (200): success, data mit origin, updated, editorMessage, optional revalidation.

Öffentlich (Vercel Deploy Hook): POST {DASHBOARD_ORIGIN}/api/public/frontend-deployments/register mit Header X-Cockpit-Register-Token: frt_… (pro Center im Dashboard erzeugt) — kein centerId nötig, Center wird am Token erkannt.

MCP: cockpit_agencyos_register_frontend_deployment

Datensicherheit: Aktualisiert nur die URL des gewählten Kanals (websitePublicUrl / …) und frontendDeploymentMeta — löscht keine Inhalte und ersetzt keine Custom Domains.

Siehe v0 Deploy ans Cockpit melden.


Homepage-Kacheln & Seiten-Inhalte (MCP-Schreiben)

Bisher nur Dashboard oder öffentliches GET — ab AgencyOS v1 auch Schreiben per API-Key (mit Audit + Revalidate):

RessourceLesenSchreiben
Homepage-KachelnGET …/centers/{centerId}/homepage-tilesPOST (neu), PATCH …/homepage-tiles/{tileId}, DELETE …/homepage-tiles/{tileId}
Page ContentGET …/centers/{centerId}/page-content (optional ?pageType=)POST (Upsert pro pageType)

MCP: cockpit_agencyos_homepage_tiles (action: list/create/update/delete), cockpit_agencyos_page_content (action: list/get/upsert). Öffentliche Vorschau weiterhin cockpit_public_homepage_tiles / cockpit_public_page_content.

Gültige pageType-Werte: u. a. ueber-uns, kontakt, anfahrt, datenschutz, impressum, shops, jobs — vollständige Liste in apps/dashboard/src/lib/page-content-api.ts.


Suche (Jobs & Offices)

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/searchcontentType unterstützt jetzt auch job und office (Komma-getrennt). MCP: cockpit_agencyos_search_content mit gleichen Parametern.


OpenAPI (Swagger) – optional nutzen

Im Repository liegt eine maschinenlesbare Spezifikation:

Empfehlung:

  • Markdown (diese Seite) bleibt die verständliche Anleitung inkl. Ablauf und Sicherheit.
  • OpenAPI lohnt sich, wenn ihr Client-Code generieren, Contract-Tests oder Swagger UI (z. B. editor.swagger.io mit Import-URL) nutzen wollt.
  • Nachteil: Zwei Quellen – bei API-Änderungen OpenAPI und diese Seite pflegen, oder langfristig die Beschreibung aus OpenAPI in die Docs einbinden (Plugin/Aufwand).

Kurz: Swagger/OpenAPI ist sinnvoll, aber nicht Pflicht. Für AgencyOS reicht zunächst diese Doku; OpenAPI ist ein komfortables Zusatzangebot.


Implementierung im Repo (Referenz)

ThemaPfad
Magic Link GET/POSTapps/dashboard/src/app/api/agencyos/magic-link/route.ts
Completeapps/dashboard/src/app/api/agencyos/magic-link/complete/route.ts
Organisationen (Session)apps/dashboard/src/app/api/agencyos/organizations/route.ts
Connect-UIapps/dashboard/src/app/agencyos/connect/page.tsx
v1 Centersapps/dashboard/src/app/api/agencyos/v1/centers/route.ts, …/v1/centers/[centerId]/route.ts
v1 Center-Kontext (KI)apps/dashboard/src/app/api/agencyos/v1/centers/[centerId]/context/route.ts
Ladelogik Kontext (Shops/Services)apps/dashboard/src/lib/integration/load-agencyos-center-context.ts
v1 Content Pushapps/dashboard/src/app/api/agencyos/v1/content/push/route.ts
v1 Push-Vorschau (dry-run)apps/dashboard/src/app/api/agencyos/v1/content/push/preview/route.ts
v1 Content-Entwürfe (Liste, Detail, Touchpoint)apps/dashboard/src/app/api/agencyos/v1/drafts/route.ts, …/drafts/[draftId]/route.ts, …/drafts/[draftId]/customer-touchpoint-suggestion/route.ts
v1 Audit-Logapps/dashboard/src/app/api/agencyos/v1/audit-logs/route.ts
v1 Center-Teamapps/dashboard/src/app/api/agencyos/v1/centers/[centerId]/team/route.ts
v1 Website-Config…/centers/[centerId]/website-config/route.ts (GET + PUT)
v1 Website-Reiter-Schema…/website-config-schema/route.ts, website-config-mcp-schema.ts, mcp-tab-hints-shared.ts, mcp-tab-hints-all-templates.ts, ilg-rgw-mcp-tab-fields.ts
v1 Homepage-Kacheln…/centers/[centerId]/homepage-tiles/route.ts, …/homepage-tiles/[tileId]/route.ts
v1 Page Content…/centers/[centerId]/page-content/route.ts
v1 Mediathek (Liste)apps/dashboard/src/app/api/agencyos/v1/media/route.ts
v1 Sucheapps/dashboard/src/app/api/agencyos/v1/search/route.ts
Push → Revalidateapps/dashboard/src/lib/integration/agencyos-content-revalidate.ts
Push-Audit (Integration)apps/dashboard/src/lib/integration/integration-audit.ts, process-center-entity-push.ts
Gemeinsame Push-Logik (mit WordPress geteilt)apps/dashboard/src/lib/integration/process-center-entity-push.ts

Verwandte Dokumentation

Nutzungsstatistik: Seitenaufrufe werden anonymisiert erfasst. Im Umami-Dashboard nach diesem Pfad filtern: /developer-guide/api-agencyos-integration