Skip to main content

AgencyOS: Connecting cockpitOS (Magic Link & Integration API)

This page is aimed at developers of AgencyOS (team product under team.cockpit-os.de). It describes what AgencyOS needs to implement to establish a trust relationship with the cockpitOS dashboard and to access Centers and content through the REST API. There are two scopes for the API key: an organization (classic) or user-wide (all Centers according to the Cockpit rights of the integrating person, across organizations including assigned Centers without an organization).

Product: AgencyOS · Dashboard Implementation (Monorepo): among others apps/dashboard/src/app/api/agencyos/…, apps/dashboard/src/app/agencyos/connect/page.tsx

Key without Magic Link (in the Dashboard): Logged-in users with appropriate permissions can create an AgencyOS integration or rotate the API key under Settings → Integrations (GET/POST /api/agencyos/integrations, POST/rotate) — useful, for example, for local tools (MCP, scripts). The complete key is provided once in the response, not permanently in the UI.

Remote-MCP (HTTP, Monorepo): The package packages/mcp-cockpit-remote provides the same MCP tools via Streamable HTTP (for organizations with Claude "Remote MCP" URL). Not part of the public dashboard API; operated with its own secret COCKPIT_MCP_HTTP_BEARER and COCKPIT_AGENCYOS_API_KEY in the server env. See package README.

Base URL

Replace {DASHBOARD_ORIGIN} with the public URL of your dashboard instance (e.g., https://dashboard.cockpit-os.de). Locally often http://localhost:3000. The variable NEXTAUTH_URL in the dashboard determines the base URL embedded in Magic Link responses.

Overview: What AgencyOS Needs to Do

  1. Request Magic Link (server-side, without user session in Cockpit): POST /api/agencyos/magic-link with integrationName and optional returnUrl.
  2. Redirect user to Cockpit: Response contains data.magicLink (path /agencyos/connect?token=…). There, an authorized person logs in and selects either an organization or "All Centers with my access rights" (user-wide key).
  3. Fetch API Key via Polling: As long as status === "pending", regularly call GET /api/agencyos/magic-link?token=…. Once status === "completed", the response provides data.apiKey (typical prefix sk_agencyos_) as well as data.accessScope ("organization" or "user") and data.organizationId (UUID or null for user-wide key).
  4. Store: Securely store the API key in AgencyOS (secret, not in logs/URLs).
  5. Use the API: All subsequent calls with Authorization: Bearer <apiKey> against /api/agencyos/v1/….

Security and Redirect

  • Never place the API key in the Redirect-URL (no query parameter with secret). Acquisition solely happens via the polling of the magic link status.
  • If you send a returnUrl (e.g., back to https://team.cockpit-os.de/...), Cockpit can redirect there after successful completion and sets agencyos=connected and cockpit_agency=connectedwithout the key. Read the key only from the GET response when status === "completed".

Difference to WordPress

AspectWordPress PluginAgencyOS
Scopeone Center per website keyOrganization: all Centers of this org · User: all Centers the integrating person has access to in Cockpit (multiple orgs + without org)
Connection UI/wordpress/connect/agencyos/connect
Content PushPOST /api/wordpress/push-content (Key = Website)POST /api/agencyos/v1/content/push with centerId in JSON
Doc Push BodyWordPress Push-ContentSame Entity Arrays (shops, events, …); see below

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

  • Auth: none (publicly like the WordPress Magic Link).
  • CORS: Access-Control-Allow-Origin: *, OPTIONS supported.

Body (JSON):

FieldTypeRequiredDescription
integrationNamestringyesDisplay name of the integration in Cockpit (e.g., "AgencyOS Production")
returnUrlstringnohttp:// or https://; optional redirect from the browser after success

Success (200):

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

Note: Token validity is 15 minutes from creation (unless completed beforehand).


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

  • Auth: none.

As long as the connection is pending, data.status is typically "pending". After completion in the browser:

  • data.status === "completed"
  • data.apiKey is set
  • data.integrationId is set
  • data.accessScope: "organization" or "user"
  • data.organizationId: UUID of the connected organization or null if accessScope === "user"

AgencyOS Implementation: Do not assume a fixed organizationId in the key. If accessScope === "user", organizationId is intentionally null; the allowed Centers result from the user's Cockpit rights (see GET /v1/centers).

Errors: e.g., 404 invalid token, 410 expired (while still pending).


3. Complete Connection in Browser (not from AgencyOS server)

This step runs in Cockpit with NextAuth session; AgencyOS normally does not call it via server-to-server.

  • UI: GET /agencyos/connect?token=…
  • Load Organizations (Session): GET /api/agencyos/organizations (for the classic variant; user-wide option is also possible without entries in the list)
  • Completion: POST /api/agencyos/magic-link/complete with JSON:
    • Organization Key: { "token": "…", "organizationId": "<uuid>" }
    • User Key: { "token": "…", "accessScope": "user" } (no organizationId needed)

The response includes apiKey, accessScope, organizationId (nullable) for immediate display in the browser — for AgencyOS, polling remains the source of truth so that the backend process reliably obtains the key.


4. AgencyOS API v1 (Bearer API Key)

All endpoints under /api/agencyos/v1/ expect:

Authorization: Bearer <apiKey>

apiKey is the AgencyOS integration key obtained from step 2 (sk_agencyos_…) — bound to an organization or user-wide (accessScope from the polling response).
CORS: Access-Control-Allow-Origin: * (including GET, POST, PATCH, OPTIONS).

Media Upload to Bunny (Images & Videos)

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

  • Auth: Authorization: Bearer <apiKey>
  • JSON (Variant A): { "url": "https://…", "folder?": "agencyos/uploads" } — Resource is loaded and copied to BunnyCDN (image or video).
  • JSON (Variant B): { "base64": "…", "mimeType?": "video/mp4", "filename?": "…", "folder?": "…" } — optionally with data:mime;base64, prefix.
  • Raw Body: Content-Type: image/* or video/* or application/octet-stream; Query ?folder=&filename= — Raw bytes sent directly to Bunny.
  • Size limits: approximately 10 MB for typical images, 100 MB for video (implementation in route.ts).
  • Response: { "success": true, "bunnyUrl": "https://…b-cdn.net/…" } (already Bunny URLs are confirmed unchanged).

MCP: Package @mall-os/mcp-cockpit-os, tool cockpit_upload_media — parameter url or base64 (plus optional mimeType, filename, folder).

List Media Library: GET {DASHBOARD_ORIGIN}/api/agencyos/v1/media?centerId=<uuid> — filter type, entityType, q, includeShared, limit, offset. MCP: cockpit_list_media.

UI (Editorial): Videos like images via FileUploadPOST /api/upload (Multipart, Bunny path e.g. centers/{centerId}/{entityType}/video/…) and “From Media Library”; not to be confused with this AgencyOS JSON route.

4.1 List Shopping Centers

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 entries, sorted by name.

For accessScope === "organization" (default):

  • all Centers with organizationId = organization of the API key, and
  • Centers without organization (organizationId: null), created by exactly this Agency integration (agencyIntegrationId = integration of the key).

For accessScope === "user":

  • all Shopping Centers that the integrating Cockpit user has access to (e.g., via UserCenterAssignment, home organization, Super roles — as defined in Cockpit), and
  • the same integrations-bound "orphan" Centers as above (without organization but agencyIntegrationId of this integration).

Thus, "free" Centers are only visible in the context of their own integration, without leaking other unbound Centers.

4.2 Create Shopping Center

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

Body (JSON) – Required fields:

FieldTypeDescription
namestring
addressstring
citystring
postalCodestring

Optional: country (default "DE"), slug (otherwise automatically from name, globally unique), description, phone, email, website, status (default "active").

Organization (as in Cockpit):

FieldTypeDescription
Standard (Organization Key)Without the following fields, organizationId will be set to the organization of the API key.
Standard (User Key)Not without target org: Either withoutOrganization/noOrganization/organizationId: null or an explicit organizationId (UUID) must be sent, for which the integrating user is authorized in Cockpit; otherwise 400/403.
withoutOrganization / noOrganizationboolean trueCenter is created without organization (organizationId: null), but remains associated with this integration (internally agencyIntegrationId), so it can continue to be used in GET/Push.
organizationIdnullSame meaning as withoutOrganization: true.
organizationIdstring (UUID)Organization Key: only allowed if the value exactly corresponds to the organization of the key; otherwise 403. User Key: allowed if the user is allowed to link this organization (as in Cockpit); otherwise 403.

Success: HTTP 201, data includes organizationId and agencyIntegrationId.

Errors: 409 if slug is already taken.

4.3 Read / Update Single Center

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

Access is allowed if the Center is permitted for this key (Organization Key: same organization or integrations-bound orphan Center; User Key: according to user rights or integrations-bound orphan Center); otherwise 404.

PATCH: only sent fields will be changed. Allows changes to name, address, city, postalCode, country, phone, email, website, description, openingHours, status, slug, websiteEnabled. Required fields cannot be set to null.

Later assign to an organization: assignToKeyedOrganization, assignToOrganization or linkToKeyedOrganization with true — only if the Center is currently without organization and comes from this integration.

  • Organization Key: The organization of the key will be linked (agencyIntegrationId will be removed).
  • User Key: Additionally attachOrganizationId (UUID) in the JSON is mandatory – target organization to which the link should be made; only if the integrating user is authorized for this in Cockpit; otherwise 403. (agencyIntegrationId will be removed.)

Website Configuration (GET + partial PUT)

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

Write: PUT {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/website-config
Partial update; templateContent will be deep-merged.

Tab Index (Schema): GET {DASHBOARD_ORIGIN}/api/agencyos/v1/website-config-schema?websiteTemplate=ilg
or ?centerId={uuid} — Dashboard tab with storage location, fields[] (exact JSON paths), v0PublicRead (public live site) and MCP hints for all website templates.

v0 / Claude Live Website (READ, without Auth):
GET …/api/centers/{centerId}/public-visitor-surfacedata.templatePublicContent (Template tab: Hero, Footer, …) + data.apiHints.pageContentGet / homepageTilesGet.
Not GET …/website-config in browser — 401. Writing remains MCP: cockpit_*. Response includes v0Integration (Read vs Write guide).

Page Content (Hero, SEO, customContent): POST …/page-content — on update, only provided scalar fields are changed; customContent will be deep-merged (e.g., customContent.ilg.anfahrtBoxes without deleting other page fields).

Recommended MCP Workflow (ILG/RGW):

  1. cockpit_website_config_schema (with websiteTemplate or centerId)
  2. Choose tab from tabs[] → read fields[] / templateContentPath / customContentPath
  3. Existing values via GET (get_center_website_config / page_content)
  4. Partial PUT/upsert only with changed keys
  5. Revalidate (automatically in API response, if configured)

MCP:

ToolFunction
cockpit_get_center_website_configFull config read
cockpit_update_center_website_configPartial update
cockpit_website_config_schemaTabs + field paths per template
cockpit_mcp_discover_toolsTool index if Claude tool_search fails

contentConfig.specialDays (Special Opening Hours): JSON Array or the same as JSON String. For each entry, e.g., { "date": "2026-12-24", "label": "Christmas Eve", "hours": { "open": "10:00", "close": "14:00" } } — closed with "hours": null. Optionally "image" (URL).

The route writes to ShoppingCenter.specialDays and additionally mirrors to openingHours.specialDays if structured openingHours (with regularHours) — analogous to storing in Cockpit "Edit Center".

4.3a Read Center Context for AI (Shops / Services)

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

Access as with GET /v1/centers/{centerId} (Bearer key, otherwise 404).

Delivers bundled read data for AgencyOS/AI, without using the public website API:

QueryDefaultMaxDescription
includecenter,shopsComma-separated: center, shops, services, news, events, offers, categories, chains, floors_summary
shopLimit5002000Max number of combined entries (individual shops + branches), sorted by name
serviceLimit200500Only if services in include
newsLimit80200Only if news in include
eventsLimit80200Only if events in include
offersLimit80200Only if offers in include

Filter: Shops and branches like GET …/centers/{centerId}/shops?publicWebsite=true&status=Active (including publication windows). Services like websiteServicePublicFilter (Center website SSR).

News / Events / Offers: All records of this center (any status, including draft), sorted by updatedAt descending — for context, deduplication and reconciliation with AgencyOS. No full texts; per line, among others id, title, slug, status, dates, source, as well as agencyosPushId and wordpressPushId from metadata, if set.

categories: Like getWebsiteShopCategories (Center + Global, minShopCount: 0, Status Active for counting) — only references for token optimization: for each entry id, name, slug (no counters/icons/colors in context).

chains: All ShopChain linked to this Center through Shops or branches (ShopLocation) (max. 500, sorted by name) — only id, name, slug.

floors_summary: For each active floor, a compact object — among others floorId, name, floorNumber, mapSvgChars (character count of mapSvg without delivering the string), suggestsHybridSvg (heuristic: typical hybrid markers in markup), hasMapImageUrl, hasShopViewBoxes, shopViewBoxesKeyCount, mapLocationActiveCount. For SVG markup, mapLocations and route planning, further the public route GET /api/wayfinding/floors?centerId= or the 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[]: compact objects including id, name, category, slug, floor, location, status, isShopLocation, chain, logo, coverImage, displayId.

4.3b Maintenance: Duplicates, Chain Merge, Domains, Center Plan

Additional AgencyOS v1 routes (Bearer, Center access as with other /v1/centers/{centerId} endpoints):

RouteMethodPurpose
…/centers/{centerId}/shop-duplicatesGETPotential shop duplicates in the Center (name similarity, query threshold 0.5–1, optional includeArchived). Response: groups with suggestedKeepId and archiveCandidateIds.
…/chains/duplicatesGETPotential ShopChain duplicates globally (query threshold, default 0.8).
…/chains/mergePOSTMerge chains (source → target): reassign branches/individual shops, delete source chain. Body: sourceChainId + targetChainId, optional dryRun, or bulk pairs[] (max. 25).
…/centers/{centerId}/verify-domainsPOSTCheck DNS/HTTPS for custom domains and update domainStatus / sslStatus in the DB.
…/centers/{centerId}/wayfinding/floorsGETCompact floor list (IDs, metrics, counts) without mapSvg body — for MCP/AI; full SVG continues GET /api/wayfinding/floors.
…/centers/{centerId}/map-locationsGET, POSTMapLocations list (floorId, activeOnly) or create.
…/centers/{centerId}/map-locations/assignPOSTAssign shop/service to SVG area (floorId, svgId, upsert).
…/map-locations/{locationId}PATCHUpdate MapLocation (assignment, geo, active/inactive).

Helper logic: apps/dashboard/src/lib/integration/ (find-shop-duplicates, find-chain-duplicates, merge-shop-chains, shop-name-similarity, agency-map-location). Duplicate archiving can be done via POST …/content/bulk-archive with contentType: "shop".

4.4 Push Content to Center

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

Same concept as WordPress Push-Content: Body with optional arrays shops, events, news, offers, services.
Additionally Required:

FieldTypeDescription
centerIdstringUUID of the Center; permissible under the same rules as GET /v1/centers/{centerId} (Organization Key vs. User Key including orphans of this integration)

Notes:

  • There is no WordPress pages storage in WordPressWebsite; focus remains on the entity arrays.
  • In Cockpit, affected content is marked with source agencyos (analogous to wordpress at the WP endpoint).

Idempotency (Events, News, Offers, Services — like Shops)

For events[], news[], offers[], services[], the same external reference applies as for shops:

  • agencyosId, externalId, clientReference or editorialReference, alternatively, with AgencyOS push a id, which is not a Cockpit UUID and not wp_*.
  • Cockpit stores the value under metadata.agencyosPushId and finds the same row for the next push for update (no duplicate).
  • Assignment order: Cockpit UUIDagencyosPushIdWordPress-wordpressPushIdslug → (only WordPress) Legacy by title.

Additionally: publishDate for news also accepts alias publishedAt or date.

Shops: Chains & AI Editorial

Editors often work in AgencyOS using natural language with AI. The API is designed such that AI does not necessarily need to know Cockpit UUIDs:

GoalRecommended Fields in shops[] entry
Do not create the same brand a hundred timesFor each logical shop, a stable reference: agencyosId or externalId (or with source push id as free string, no UUID) — stored in Cockpit under metadata.agencyosPushId and used for update on the next push. Additionally slug per Center, if available.
"Deichmann is a chain"chain, brand, chainName, brandName or shopChainName with the brand name → Cockpit connects the shop to this ShopChain (if it does not exist, it is created; only new chain data, no deletions).
Already known chain (UUID from Cockpit)Set shopChainId or chainId.
Only individual shop without a chainstandaloneShop / individualShop / clearShopChain: true or mode: "individual" / "standalone".

Professional note: In Cockpit, there are also branches as ShopLocation (chain + Center). The push initially lands on the Shop including shopChainId link; a complete mirroring of branches is separate from this and can be added later if needed.

Detailed field list (including WordPress): WordPress Push-Content – shops.

Response: analogous to WordPress: success, data.summary, optional data.errors.

4.4a Push Preview (no DB Change)

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

  • Same JSON body as POST …/content/push (including centerId), same Bearer API Key and Center access.
  • No create/update write operations in the database — suitable for approval workflows (e.g., AgencyOS shows the planned changes, then actual push).
  • Response (200): success, message, data.previewPlan (list of planned steps with entity, action create/update, match for assignment, existingId, planned, optionally notes), data.summary, optional data.errors (validation/chain errors like with the actual push).
  • Shop Chains: If a new ShopChain would be created (name-based resolution), this appears in planned.shopChain as would_create_chain without creating the chain; note possibly in notes.
  • GET …/preview: is not supported (405) — JSON payload belongs in the POST body.

Content Drafts (Workflow) — Read, Approval, Customer Contact

Same ContentDraft logic as under Workflow & Approvals in the dashboard, via AgencyOS key:

MethodPathShort Description
GET/api/agencyos/v1/draftsList; Query as before (status, contentType, centerId, campaignLabel, limit). New: includeData=1 — parsed data per entry (lengths server-side limited), including customerCommunication, dispatchItemId, content fields.
GET/api/agencyos/v1/drafts/{draftId}One draft; optionally ?includeData=1.
PUT/api/agencyos/v1/drafts/{draftId}approve / reject (unchanged).
POST/api/agencyos/v1/drafts/{draftId}/customer-touchpoint-suggestionBody: { "scenario": "workflow_approve" | "workflow_reject" | "workflow_pending", "rejectionReason"?: string }same AI suggestions as in the dashboard (subject, email draft, internal notes). Uses the global Cockpit AI configuration.

MCP/Claude: cockpit_list_drafts (optional includeData: true), cockpit_get_draft, cockpit_draft_customer_touchpoint, cockpit_update_draft. Per draft: createdBy, createdByName, source, createdAt.


Audit Log (Provenance)

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

Bearer API key; Center access as with other v1 routes.

QueryDescription
centerIdLast changes in the Center (daily overview) — or
entityType + entityIdHistory of an entry (offer, news, shop, event, service, job, center, shop-location)
limitDefault 50, max 100
includeValues1 / true — include oldValues / newValues (field changes)

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

MCP: cockpit_audit_logs — e.g. "Who last edited this offer?" (entityType + entityId from search_content). With summary: true / query summary=1: leaderboard byUser (ideal for "who was most active?"). From deploy: also MCP push, uploads and center creation via AgencyOS will be logged (metadata.integrationName, channel: agencyos).


Center Team (Access & Roles)

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

Bearer API key; Center access as with other v1 routes.

Response (200): center, count, data[] with assigned users (user.name, user.email), centerRole, content/shop permissions, assignedAt.

MCP: cockpit_center_team — e.g. "How many people have access to Center X?"


Website Cache (Revalidate)

After POST …/content/push (Direct push, no drafts), the dashboard automatically invalidates the Next.js cache on all instances listed in CENTER_WEBSITE_URLS / CENTER_WEBSITE_URL — if REVALIDATION_SECRET is set. The push response can include data.revalidation (instances[] per URL).

Manually: POST {DASHBOARD_ORIGIN}/api/agencyos/v1/centers/{centerId}/revalidate (Bearer key, body optional { "fullRevalidation": true }).

MCP: cockpit_revalidate_website

v0/Vercel: Live site needs app/api/revalidate/route.ts, REVALIDATION_SECRET on Vercel (identical to the dashboard) and websitePublicUrl in the Center (automated via deploy registration or manually in the dashboard). Legacy: global CENTER_WEBSITE_URLS. Without revalidate-secret: data in Cockpit is current, Vercel may show ISR cache.


Website Live Inventory (Bulk)

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/website-live-inventory

Bearer API key; returns the website live status for all accessible centers (max. 500) in one call — instead of polling frontend-channels per center.

Query parameters (optional):

ParameterDescription
chainSlug / chainNameAs with GET …/centers — only centers with a location of this chain
organizationId / organizationNameOnly centers of one organization
websiteEnabledOnly=trueOnly centers with website enabled
excludeTestLike=trueHide test/demo centers (name/slug)

Response (200): success, data.items[], data.summary, meta.

Per entry in items:

FieldMeaning
bucketoff | cockpit_only | v0_preview | v0_production | custom_domain_live
isV0WebsiteConnectedv0/Vercel deploy reported?
urls.cockpitPreview / v0Staging / v0Production / customDomainChannel URLs
urls.activeSurfaceEffective visitor surface (priority: Production → Domain → Staging → Cockpit)
domainStatus / domainVerifiedDNS/domain status
websiteStackOverallLeveloff | configured | pending | active | warning

summary.byBucket counts centers per class; summary.v0ConnectedCount and customDomainActiveCount for quick KPIs.

MCP: cockpit_website_live_inventory — same filters as tool parameters.

Note: No HTTP ping on live URLs — classification from Cockpit data (as in the dashboard “Frontend Channels”). For single-center detail, use cockpit_frontend_channels or cockpit_dns_status.


Frontend Deployment Registration (v0 → Cockpit)

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

Bearer API key; Center access as with other v1 routes.

Body (JSON):

FieldTypeRequiredDescription
channelstringyeswebsite | signage | companion
originstringyesDeployment origin without path, e.g. https://xyz.vercel.app
sourcestringnoe.g. vercel, v0, mcp

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

Public (Vercel Deploy Hook): POST {DASHBOARD_ORIGIN}/api/public/frontend-deployments/register with header X-Cockpit-Register-Token: frt_… (generated per Center in the dashboard) — no centerId needed, Center is recognized by the token.

MCP: cockpit_register_frontend_deployment

Data Security: Only updates the URL of the selected channel (websitePublicUrl / …) and frontendDeploymentMeta — does not delete content or replace custom domains.

See report v0 deploy to Cockpit.


Homepage Tiles & Page Content (MCP Writing)

Previously only dashboard or public GET — from AgencyOS v1 also writing via API key (with audit + revalidate):

ResourceReadWrite
Homepage TilesGET …/centers/{centerId}/homepage-tilesPOST (new), PATCH …/homepage-tiles/{tileId}, DELETE …/homepage-tiles/{tileId}
Page ContentGET …/centers/{centerId}/page-content (optional ?pageType=)POST (upsert per pageType)

MCP: cockpit_homepage_tiles (action: list/create/update/delete), cockpit_page_content (action: list/get/upsert). Public preview still cockpit_public_homepage_tiles / cockpit_public_page_content.

Valid pageType values: including about-us, contact, directions, data-protection, imprint, shops, jobs — full list in apps/dashboard/src/lib/page-content-api.ts.


Search (Jobs & Offices)

GET {DASHBOARD_ORIGIN}/api/agencyos/v1/searchcontentType now also supports job and office (comma-separated). MCP: cockpit_search_content with the same parameters.


OpenAPI (Swagger) – optional use

In the repository, there is a machine-readable specification:

Recommendation:

  • Markdown (this page) remains the understandable guide including process and security.
  • OpenAPI is worthwhile if you want to generate client code, run contract tests or use Swagger UI (e.g., editor.swagger.io with import URL).
  • Disadvantage: two sources – for API changes, both OpenAPI and this page need to be maintained, or long-term integratethe description from OpenAPI into the docs (plugin effort).

In short: Swagger/OpenAPI is useful but not mandatory. For AgencyOS, this documentation is initially sufficient; OpenAPI is a convenient additional offer.


Implementation in Repo (Reference)

TopicPath
Magic Link GET/POSTapps/dashboard/src/app/api/agencyos/magic-link/route.ts
Completeapps/dashboard/src/app/api/agencyos/magic-link/complete/route.ts
Organizations (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 Context (AI)apps/dashboard/src/app/api/agencyos/v1/centers/[centerId]/context/route.ts
Load Logic Context (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 Preview (dry-run)apps/dashboard/src/app/api/agencyos/v1/content/push/preview/route.ts
v1 Content Drafts (List, 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 Tab 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 Tiles…/centers/[centerId]/homepage-tiles/route.ts, …/homepage-tiles/[tileId]/route.ts
v1 Page Content…/centers/[centerId]/page-content/route.ts
v1 Media Library (List)apps/dashboard/src/app/api/agencyos/v1/media/route.ts
v1 Searchapps/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
Shared Push Logic (shared with WordPress)apps/dashboard/src/lib/integration/process-center-entity-push.ts

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