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.
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
- Request Magic Link (server-side, without user session in Cockpit):
POST /api/agencyos/magic-linkwithintegrationNameand optionalreturnUrl. - 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). - Fetch API Key via Polling: As long as
status === "pending", regularly callGET /api/agencyos/magic-link?token=…. Oncestatus === "completed", the response providesdata.apiKey(typical prefixsk_agencyos_) as well asdata.accessScope("organization"or"user") anddata.organizationId(UUID ornullfor user-wide key). - Store: Securely store the API key in AgencyOS (secret, not in logs/URLs).
- 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 tohttps://team.cockpit-os.de/...), Cockpit can redirect there after successful completion and setsagencyos=connectedandcockpit_agency=connected— without the key. Read the key only from the GET response whenstatus === "completed".
Difference to WordPress
| Aspect | WordPress Plugin | AgencyOS |
|---|---|---|
| Scope | one Center per website key | Organization: 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 Push | POST /api/wordpress/push-content (Key = Website) | POST /api/agencyos/v1/content/push with centerId in JSON |
| Doc Push Body | WordPress Push-Content | Same Entity Arrays (shops, events, …); see below |
1. Create Magic Link
POST {DASHBOARD_ORIGIN}/api/agencyos/magic-link
- Auth: none (publicly like the WordPress Magic Link).
- CORS:
Access-Control-Allow-Origin: *,OPTIONSsupported.
Body (JSON):
| Field | Type | Required | Description |
|---|---|---|---|
integrationName | string | yes | Display name of the integration in Cockpit (e.g., "AgencyOS Production") |
returnUrl | string | no | http:// 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).
2. Query Magic Link Status (Polling)
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.apiKeyis setdata.integrationIdis setdata.accessScope:"organization"or"user"data.organizationId: UUID of the connected organization ornullifaccessScope === "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/completewith JSON:- Organization Key:
{ "token": "…", "organizationId": "<uuid>" } - User Key:
{ "token": "…", "accessScope": "user" }(noorganizationIdneeded)
- Organization Key:
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 withdata:mime;base64,prefix. - Raw Body:
Content-Type: image/*orvideo/*orapplication/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 FileUpload → POST /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
agencyIntegrationIdof 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:
| Field | Type | Description |
|---|---|---|
name | string | |
address | string | |
city | string | |
postalCode | string |
Optional: country (default "DE"), slug (otherwise automatically from name, globally unique), description, phone, email, website, status (default "active").
Organization (as in Cockpit):
| Field | Type | Description |
|---|---|---|
| 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 / noOrganization | boolean true | Center is created without organization (organizationId: null), but remains associated with this integration (internally agencyIntegrationId), so it can continue to be used in GET/Push. |
organizationId | null | Same meaning as withoutOrganization: true. |
organizationId | string (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 (
agencyIntegrationIdwill 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. (agencyIntegrationIdwill 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-surface → data.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):
cockpit_website_config_schema(withwebsiteTemplateorcenterId)- Choose tab from
tabs[]→ readfields[]/templateContentPath/customContentPath - Existing values via GET (
get_center_website_config/page_content) - Partial PUT/upsert only with changed keys
- Revalidate (automatically in API response, if configured)
MCP:
| Tool | Function |
|---|---|
cockpit_get_center_website_config | Full config read |
cockpit_update_center_website_config | Partial update |
cockpit_website_config_schema | Tabs + field paths per template |
cockpit_mcp_discover_tools | Tool 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:
| Query | Default | Max | Description |
|---|---|---|---|
include | center,shops | — | Comma-separated: center, shops, services, news, events, offers, categories, chains, floors_summary |
shopLimit | 500 | 2000 | Max number of combined entries (individual shops + branches), sorted by name |
serviceLimit | 200 | 500 | Only if services in include |
newsLimit | 80 | 200 | Only if news in include |
eventsLimit | 80 | 200 | Only if events in include |
offersLimit | 80 | 200 | Only 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):
| Route | Method | Purpose |
|---|---|---|
…/centers/{centerId}/shop-duplicates | GET | Potential shop duplicates in the Center (name similarity, query threshold 0.5–1, optional includeArchived). Response: groups with suggestedKeepId and archiveCandidateIds. |
…/chains/duplicates | GET | Potential ShopChain duplicates globally (query threshold, default 0.8). |
…/chains/merge | POST | Merge chains (source → target): reassign branches/individual shops, delete source chain. Body: sourceChainId + targetChainId, optional dryRun, or bulk pairs[] (max. 25). |
…/centers/{centerId}/verify-domains | POST | Check DNS/HTTPS for custom domains and update domainStatus / sslStatus in the DB. |
…/centers/{centerId}/wayfinding/floors | GET | Compact floor list (IDs, metrics, counts) without mapSvg body — for MCP/AI; full SVG continues GET /api/wayfinding/floors. |
…/centers/{centerId}/map-locations | GET, POST | MapLocations list (floorId, activeOnly) or create. |
…/centers/{centerId}/map-locations/assign | POST | Assign shop/service to SVG area (floorId, svgId, upsert). |
…/map-locations/{locationId} | PATCH | Update 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:
| Field | Type | Description |
|---|---|---|
centerId | string | UUID 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
pagesstorage inWordPressWebsite; focus remains on the entity arrays. - In Cockpit, affected content is marked with
sourceagencyos(analogous towordpressat 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,clientReferenceoreditorialReference, alternatively, with AgencyOS push aid, which is not a Cockpit UUID and notwp_*.- Cockpit stores the value under
metadata.agencyosPushIdand finds the same row for the next push for update (no duplicate). - Assignment order: Cockpit UUID →
agencyosPushId→ WordPress-wordpressPushId→ slug → (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:
| Goal | Recommended Fields in shops[] entry |
|---|---|
| Do not create the same brand a hundred times | For 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 chain | standaloneShop / 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(includingcenterId), same Bearer API Key and Center access. - No
create/updatewrite 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 withentity,actioncreate/update,matchfor assignment,existingId,planned, optionallynotes),data.summary, optionaldata.errors(validation/chain errors like with the actual push). - Shop Chains: If a new
ShopChainwould be created (name-based resolution), this appears inplanned.shopChainaswould_create_chainwithout creating the chain; note possibly innotes. 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:
| Method | Path | Short Description |
|---|---|---|
| GET | /api/agencyos/v1/drafts | List; 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-suggestion | Body: { "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.
| Query | Description |
|---|---|
centerId | Last changes in the Center (daily overview) — or |
entityType + entityId | History of an entry (offer, news, shop, event, service, job, center, shop-location) |
limit | Default 50, max 100 |
includeValues | 1 / 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):
| Parameter | Description |
|---|---|
chainSlug / chainName | As with GET …/centers — only centers with a location of this chain |
organizationId / organizationName | Only centers of one organization |
websiteEnabledOnly=true | Only centers with website enabled |
excludeTestLike=true | Hide test/demo centers (name/slug) |
Response (200): success, data.items[], data.summary, meta.
Per entry in items:
| Field | Meaning |
|---|---|
bucket | off | cockpit_only | v0_preview | v0_production | custom_domain_live |
isV0WebsiteConnected | v0/Vercel deploy reported? |
urls.cockpitPreview / v0Staging / v0Production / customDomain | Channel URLs |
urls.activeSurface | Effective visitor surface (priority: Production → Domain → Staging → Cockpit) |
domainStatus / domainVerified | DNS/domain status |
websiteStackOverallLevel | off | 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):
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | yes | website | signage | companion |
origin | string | yes | Deployment origin without path, e.g. https://xyz.vercel.app |
source | string | no | e.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):
| Resource | Read | Write |
|---|---|---|
| Homepage Tiles | GET …/centers/{centerId}/homepage-tiles | POST (new), PATCH …/homepage-tiles/{tileId}, DELETE …/homepage-tiles/{tileId} |
| Page Content | GET …/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/search — contentType 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:
- Files:
/openapi/agencyos-integration.yaml(AgencyOS v1 — includingGET …/contextwith optionalfloors_summary),/openapi/public-wayfinding-read.yaml(public wayfinding read without key:floors,centerplan)
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)
| Topic | Path |
|---|---|
| Magic Link GET/POST | apps/dashboard/src/app/api/agencyos/magic-link/route.ts |
| Complete | apps/dashboard/src/app/api/agencyos/magic-link/complete/route.ts |
| Organizations (Session) | apps/dashboard/src/app/api/agencyos/organizations/route.ts |
| Connect-UI | apps/dashboard/src/app/agencyos/connect/page.tsx |
| v1 Centers | apps/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 Push | apps/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 Log | apps/dashboard/src/app/api/agencyos/v1/audit-logs/route.ts |
| v1 Center Team | apps/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 Search | apps/dashboard/src/app/api/agencyos/v1/search/route.ts |
| Push → Revalidate | apps/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 |
Related Documentation
- AI Website Building & Cockpit Sync (Concept & Phases) – Target image Greenfield/Brownfield, existing components, roadmap without data loss
- WordPress Push-Content – Detailed field lists for
shops/events/news/offers/services - Center Plan WordPress Integration – Context WordPress (not AgencyOS)
Nutzungsstatistik: Seitenaufrufe werden anonymisiert erfasst. Im Umami-Dashboard nach diesem Pfad filtern: /en/developer-guide/api-agencyos-integration