Capability discovery
Preview A managed.dev site carries a runtime —
wordpress, static, and, on the roadmap, drupal and more. Rather than make you
hard-code what each runtime can do, the API lets a site tell you: a machine-readable
capability map you read before you act. This is what lets one integration work across
every runtime, and what lets new runtimes inherit the whole platform without you
shipping new code.
The runtime field
Section titled “The runtime field”Every site exposes a runtime attribute. The foundational resources — sites,
environments, deployments, domains, backups, jobs, observability — are defined
without reference to any CMS, so they work identically whichever runtime a site
runs. What differs between a WordPress site and a static site isn’t the resource model;
it’s the set of capabilities each advertises.
GET /v1/sites/{id}/capabilities
Section titled “GET /v1/sites/{id}/capabilities”GET /v1/sites/{site_id}/capabilities
Returns the capability map for one site — which dynamic-layer features its runtime supports, and for each, the actions you can take:
{ "data": { "runtime": "wordpress", "runtime_version": "6.8", "capabilities": { "components.plugins": { "supported": true, "actions": ["list","install","activate","update","delete"] }, "components.themes": { "supported": true, "actions": ["list","activate","update","delete"] }, "components.users": { "supported": true }, "components.content": { "supported": true }, "cron": { "supported": true, "kind": "wp-cron" }, "database": { "supported": true, "engine": "mysql" }, "exec": { "supported": true, "shells": ["wp-cli","bash"] }, "magic_link": { "supported": true }, "clone_content": { "supported": true, "selectors": ["db","files"] }, "build": { "supported": true } } }, "request_id": "req_01J9…"}For a static site, the CMS-shaped entries report supported: false:
{ "data": { "runtime": "static", "capabilities": { "components.plugins": { "supported": false }, "database": { "supported": false }, "magic_link": { "supported": false }, "build": { "supported": true }, "clone_content": { "supported": true, "selectors": ["files"] } } }, "request_id": "req_01J9…"}The pattern for a client is: read capabilities, then drive your UI and tool selection
from supported and actions — show the “install plugin” action only where
components.plugins.supported is true and "install" is in its actions.
GET /v1/runtimes — the static catalog
Section titled “GET /v1/runtimes — the static catalog”GET /v1/runtimes
Where …/capabilities answers “what can this site do?”, /v1/runtimes answers “what
do runtimes do in general?” It’s a static catalog — each runtime mapped to its default
capabilities and component kinds — useful for building a runtime picker or validating a
plan before any site exists:
curl https://api.managed.dev/v1/runtimes \ -H "Authorization: Bearer mfk_live_…" \ -H "Forge-Version: 2026-06-23"{ "data": [ { "runtime": "wordpress", "component_kinds": ["plugin","theme","user","content","cron"], "default_capabilities": ["components.plugins","database","exec","magic_link"] }, { "runtime": "static", "component_kinds": [], "default_capabilities": ["build","clone_content"] } ], "request_id": "req_01J9…"}See runtimes & capabilities reference for the complete catalog.
Two ways the API says “no”
Section titled “Two ways the API says “no””When you call a capability-gated route, a refusal can mean one of two distinct things — and the status code tells you which:
| Status | Meaning | Example |
|---|---|---|
404 not_found |
The capability is structurally absent for this runtime — it doesn’t exist here, so the API hides it (a scoped key couldn’t even hold the scope for it). | A plugins route on a runtime that has no concept of plugins. |
409 capability.unsupported |
The route exists, but this runtime can’t perform it. | POST …/components { kind: "plugin" } against a static site. |
The distinction matters when you write error handling: a 404 says “don’t offer this
feature for this runtime at all”; a 409 capability.unsupported says “this is a real
feature, just not one this particular site can do.” Both are different from a plain
permissions 403 — see errors.
Why this is the foundation
Section titled “Why this is the foundation”Capability discovery is what makes managed.dev runtime-agnostic by construction. Adding a runtime requires no change to the core API — only a new catalog entry and the agent’s capability probe. Every client that discovers capabilities rather than hard-coding them picks up the new runtime for free. That’s the whole trick: a static site and a WordPress site are the same resource type with different advertised capabilities.