Skip to main content

Audit: Namespace /api/content/*

Status: Internal inventory after the refactor of the Session/Center lists (/api/news, /api/events, …).
Objective: Transparency regarding which routes are publicly semi-open and where without centerId all records may potentially be visible.

Legend Risk: “low” = expected behavior or session protected; “medium/high” = data leakage possible without authentication or without Center parameter.


GET Lists (classic “Content API” style, CORS *)

RouteSessionCenter if no centerIdMain CallerRisk & Note
GET /api/content/eventsnoall Events in the systemree-carree-event-banner-section-config, content-selector-dialog (with filter publicOnWebsite + usually centerId), demo scripthigh: unauthenticated; without ?centerId= complete table. With centerId only this center. For website/dialog typically always centerId.
GET /api/content/newsnoall NewsRee-Carree, Goldbeck-Hero (centerId+published), content-selector-dialog, demo scripthigh as above. published=true reduces content-wise, not tenant-wise.
GET /api/content/offersnoall OffersRee-Carree, content-selector-dialog, demo scripthigh as above.
GET /api/content/jobsnoall Jobscontent-selector-dialog, demo scripthigh as above.
GET /api/content/servicesnoall Servicescontent-selector-dialog, demo scripthigh as above.
GET /api/content/shop-chainsnoall ShopChains including locationsDemo script (create-cms-demo-data.js)very high for production URL: global chain list without session.

Design Note: These endpoints are historically embedding/widget-friendly (CORS, no login). Tenant separation is only done via ?centerId= — those omitting the parameter see system-wide.


Dashboard-bound (Session + Center Logic)

RouteSessionCenter/Scope LogicMain CallerRisk & Note
GET /api/content/metricsyes (without session: 401, unless otherwise documented in future)mergeSessionCenterScopeIntoListParams + pickCenterWhereFragment/dashboard/content (cookie), MCP optional /api/content/metricslow after fix.
GET /api/content/recentyes (without session: 401)as above/dashboard/contentlow after fix.
GET /api/content/plannerrequireSessiongetCenterIdsForUser + query centerId / organizationId/dashboard/content/plannerlow
GET /api/content/draftsrequireSessiongetCenterIdsForUserPendingDraftsSection, workflow pagelow
GET/PUT/DELETE …/drafts/[draftId]sessionaccess via draft/center (route checks)workflow, pending draftslow (details in route)
POST …/customer-touchpoint-suggestionsessionworkflowlow
POST /api/content/reclassifysessiontransaction checks entitiescontent reclassify dialoglow

Other POST/Admin

RouteSessionNoteRisk
POST /api/content/drafts/retry-approvedyesProcesses all drafts with status APPROVED (no center filter in the WHERE clause) — deliberately an admin tool; may restrict by center later.medium (operational: too broad)
`POST /api/content/eventsnewsoffersjobs

Discrepancy / Bug in Frontend (outside /api/content/)

FindingLocationRecommendation
Incorrect path /api/content/hot-picks does not existeditor-dashboard.tsx (Hot-Picks statistics)Change to /api/hot-picks (or alias only if deliberately desired). Currently very likely 404 / empty data.

Comparison with Protected List APIs

For logged-in editing, the dashboard predominantly uses:

  • /api/news, /api/events, /api/offers, /api/jobs, /api/shops, /api/services — with mergeSessionCenterScope or equivalent offer filter.

The namespace /api/content/… (GET legacy) remains not automatically equally secure — deliberately or due to technical fault.


  1. Production: Evaluate GET /api/content/shop-chains and anonymous GET without centerId — potentially API key, IP allowlist, or deprecation in favor of only centerId + public filters.
  2. editor-dashboard: Correct Hot-Picks URL.
  3. Optional: Merge public readers under e.g. /api/public/centers/[id]/… and gradually shut down /api/content/* GET or only allow with signature.

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