Sites
A site is the product-agnostic root of everything you deploy. It carries a
runtime (wordpress, static, or wasm — with drupal and node on the
roadmap), owns one or more
environments, and advertises what it can do through
capability discovery. A static site and a
WordPress site are the same resource type — they differ only in their advertised
capabilities.
The site object
Section titled “The site object”| Field | Type | Description |
|---|---|---|
id |
string | Stable identifier, e.g. site_01J7.... |
slug |
string | Server-minted, URL-safe identifier. |
domain |
string | The site’s primary domain. |
profile |
string | Build profile: bedrock, vanilla, static, or wasm. |
php_version |
string | 8.2, 8.3, 8.4, or 8.5 on PHP profiles. |
runtime |
string | wordpress, static, or wasm — drives capability discovery. |
region |
string | Hosting region, e.g. us-east. |
git_repo / git_branch |
string | null | The connected repository on git-managed sites. |
team_id / project_id |
string | null | The owning team and project; both null on a personal, unassigned site. |
status |
string | provisioning, running, stopped, deleting, deleted, failed, or awaiting_push. |
status_reason |
string | null | Why the site entered failed — the last error from the driving job; null otherwise. |
recoverable_until |
string | null | A deleted site can be recovered until this timestamp. |
created_at |
string | RFC 3339 timestamp. |
List & create sites
Section titled “List & create sites”GET /v1/sites
Section titled “GET /v1/sites”sites:read List the sites you can access, cursor-paginated, newest first.
POST /v1/sites
Section titled “POST /v1/sites”sites:write Create a site. Provisioning is
non-instant, so this returns a 202 Accepted with an
async job and a Location header.
| Parameter | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The site’s primary domain; must be unique on the platform. |
profile |
string | yes | bedrock, vanilla, static, or wasm. |
php_version |
string | unless static |
8.2, 8.3, 8.4, or 8.5; defaults to 8.3. |
region |
string | no | Hosting region; defaults to us-east. |
git_repo |
string | for bedrock |
An https or ssh git URL — the bedrock profile deploys from git. |
git_branch |
string | no | The branch that deploys to production. |
team_id |
string | no | Create the site under a team you own or admin; omit for a personal site. |
curl -X POST https://api.managed.dev/v1/sites \ -H "Authorization: Bearer mfk_live_..." \ -H "Idempotency-Key: 4a7b...e2" \ -H "Content-Type: application/json" \ -d '{ "domain": "acme-store.com", "profile": "bedrock", "php_version": "8.3", "git_repo": "https://github.com/acme/store.git", "git_branch": "main" }'repo, branch := "https://github.com/acme/store.git", "main"job, err := client.Sites.Create(ctx, forge.SiteCreateParams{ Domain: "acme-store.com", Profile: "bedrock", PHPVersion: "8.3", GitRepo: &repo, GitBranch: &branch,})mf sites create --domain acme-store.com \ --profile bedrock --php 8.3 \ --git-repo https://github.com/acme/store.git --git-branch main{ "data": { "id": "job_01J9...", "type": "deployment.deploy", "status": "queued", "progress": 0, "created_at": "2026-06-24T14:10:02Z", "resource": { "type": "deployment", "id": "site_01J7...", "site_id": "site_01J7..." }, "result": null, "error": null, "links": { "self": "/v1/jobs/job_01J9...", "stream": "/v1/jobs/job_01J9.../stream" } }, "request_id": "req_01J9..."}Retrieve & delete a site
Section titled “Retrieve & delete a site”GET /v1/sites/{siteID}
Section titled “GET /v1/sites/{siteID}”sites:read Retrieve a single site.
curl https://api.managed.dev/v1/sites/site_01J7... \ -H "Authorization: Bearer mfk_live_..."{ "data": { "id": "site_01J7...", "slug": "acme-store", "domain": "acme-store.com", "profile": "bedrock", "php_version": "8.3", "runtime": "wordpress", "region": "us-east", "git_repo": "https://github.com/acme/store.git", "git_branch": "main", "team_id": "team_01J6...", "project_id": "proj_01J6...", "status": "running", "status_reason": null, "created_at": "2026-06-24T14:10:09Z" }, "request_id": "req_01J9..."}DELETE /v1/sites/{siteID}
Section titled “DELETE /v1/sites/{siteID}”sites:admin Permanently delete a site. The request
body must carry a typed confirmation, and the teardown runs as a 202
job (type site.delete).
| Parameter | Type | Required | Description |
|---|---|---|---|
confirm_domain |
string | yes | Must equal the site’s domain — typed confirmation for an irreversible destroy. |
curl -X DELETE https://api.managed.dev/v1/sites/site_01J7... \ -H "Authorization: Bearer mfk_live_..." \ -H "Content-Type: application/json" \ -d '{ "confirm_domain": "acme-store.com" }'job, err := client.Sites.Delete(ctx, "site_01J7...", "acme-store.com")mf sites delete site_01J7... --confirm-domain acme-store.comLifecycle actions
Section titled “Lifecycle actions”| Endpoint | Scope | What it does |
|---|---|---|
POST /v1/sites/{siteID}/restart |
sites:write | Restart the site’s containers; returns a 202 job (site.restart). |
POST /v1/sites/{siteID}/php-version |
sites:write | Switch the site’s PHP version; returns a 202 job. |
POST /v1/sites/{siteID}/transfer |
sites:admin | Move ownership to another team or your personal account — synchronous. |
curl -X POST https://api.managed.dev/v1/sites/site_01J7.../restart \ -H "Authorization: Bearer mfk_live_..." \ -H "Idempotency-Key: 1f0c...77"Switch PHP version
Section titled “Switch PHP version”POST /v1/sites/{siteID}/php-version moves the site — and every environment — onto
the platform image for the requested PHP version, then runs a post-switch health
probe. The profile is immutable; php_version is the switch axis. Not available on
static or wasm sites.
| Parameter | Type | Required | Description |
|---|---|---|---|
version |
string | yes | Target version: 8.2, 8.3, 8.4, or 8.5. |
dry_run |
boolean | no | Preview the old → new image and the affected environments without changing anything. |
force |
boolean | no | Override the Bedrock composer.lock constraint guard — may break the site. |
On Bedrock sites the platform checks your composer.lock PHP constraint first: a
switch your lockfile forbids returns 400 invalid_request unless you pass force.
Start with dry_run to see exactly what would change.
curl -X POST https://api.managed.dev/v1/sites/site_01J7.../php-version \ -H "Authorization: Bearer mfk_live_..." \ -H "Idempotency-Key: 5c1d...9b" \ -H "Content-Type: application/json" \ -d '{ "version": "8.4", "dry_run": true }'dry := truejob, err := client.Sites.SwitchPHPVersion(ctx, "site_01J7...", forge.PHPVersionSwitchParams{Version: "8.4", DryRun: &dry})mf sites php-version site_01J7... 8.4 --dry-runTransfer a site
Section titled “Transfer a site”POST /v1/sites/{siteID}/transfer flips ownership immediately — it is one of the few
mutations that is synchronous rather than a job. Provide exactly one destination:
| Parameter | Type | Required | Description |
|---|---|---|---|
to_team_id |
string | one of | Destination team — you must be its owner or admin. |
to_client_id |
string | one of | Your own personal account id, to take a team site personal. |
curl -X POST https://api.managed.dev/v1/sites/site_01J7.../transfer \ -H "Authorization: Bearer mfk_live_..." \ -H "Content-Type: application/json" \ -d '{ "to_team_id": "team_01J6..." }'{ "data": { "site_id": "site_01J7...", "functions_transferred": 2 }, "request_id": "req_01J9..."}Any functions attached to the site are re-owned with it —
functions_transferred reports how many moved.
Configuration
Section titled “Configuration”GET · PATCH /v1/sites/{siteID}/config
Section titled “GET · PATCH /v1/sites/{siteID}/config”Read (sites:read) or update
(sites:write) the site’s configuration. A PATCH
applies asynchronously and returns a 202 job (type
site.config_apply).
| Block | Fields |
|---|---|
security |
coraza_mode (on | detection | off), crowdsec_enabled, login_lockout_enabled, allowed_ips, patchstack_client_id, patchstack_secret_key — see WAF. |
performance |
page_cache_ttl, frankenphp_workers, hot_page_count, hot_ttl_seconds, hot_refresh_interval_seconds. |
observability |
trace_sample_rate. |
curl https://api.managed.dev/v1/sites/site_01J7.../config \ -H "Authorization: Bearer mfk_live_..."Branch routes
Section titled “Branch routes”GET /v1/sites/{siteID}/branch-routes
Section titled “GET /v1/sites/{siteID}/branch-routes”sites:read Read the site’s branch-to-environment routing rules — which branches deploy to production, staging, or a preview, and which are ignored. See branch routes.
{ "data": { "rules": [ { "pattern": "main", "target": "live" }, { "pattern": "develop", "target": "staging" }, { "pattern": "feature/*", "target": "preview" }, { "pattern": "dependabot/*", "target": "ignore" } ] }, "request_id": "req_01J9..."}Redirects
Section titled “Redirects”GET · PUT /v1/sites/{siteID}/redirects
Section titled “GET · PUT /v1/sites/{siteID}/redirects”Read (redirects:read) or replace
(redirects:write) the site’s edge redirect rules.
PUT replaces the entire rule set and applies it via a 202
job — there is no per-rule patch, so read, modify, and
put back the full list.
| Field | Type | Description |
|---|---|---|
from |
string | Source path, e.g. /old — path-matched at the edge. |
to |
string | Destination path or absolute http(s) URL. |
code |
integer | 301, 302, 307, or 308. |
curl -X PUT https://api.managed.dev/v1/sites/site_01J7.../redirects \ -H "Authorization: Bearer mfk_live_..." \ -H "Idempotency-Key: 88af...12" \ -H "Content-Type: application/json" \ -d '{ "redirects": [ { "from": "/old-shop", "to": "/shop", "code": 301 }, { "from": "/promo", "to": "https://campaign.acme-store.com", "code": 302 } ] }'mf sites redirects set site_01J7... --json redirects.jsonTLS status
Section titled “TLS status”GET /v1/sites/{siteID}/tls
Section titled “GET /v1/sites/{siteID}/tls”tls:read The site’s TLS certificate status —
state (unconfigured, not_registered, pending, valid, or expired), the
issuing CA, and issued_at / expires_at. Certificates are issued and renewed
automatically; see TLS.
mf sites tls site_01J7...Snapshots
Section titled “Snapshots”GET /v1/sites/{siteID}/snapshots
Section titled “GET /v1/sites/{siteID}/snapshots”backups:read List the site’s backup snapshots, cursor-paginated. Taking and restoring snapshots is documented on backups & snapshots.
Audit feed
Section titled “Audit feed”GET /v1/sites/{siteID}/audit
Section titled “GET /v1/sites/{siteID}/audit”audit:read Every audited action performed on this site, newest first.
| Parameter | Type | Required | Description |
|---|---|---|---|
event |
string | no | Filter to a single event type, e.g. site_config_patch. |
actor |
string | no | Filter to events performed by one principal. |
since / until |
string | no | RFC 3339 window — since inclusive, until exclusive. |
limit / cursor |
— | no | Pagination. |
mf sites audit site_01J7... --since 2026-06-17T00:00:00Z --limit 50The event shape is shared with the account and team feeds — see the audit log reference.
Capabilities
Section titled “Capabilities”GET /v1/sites/{siteID}/capabilities
Section titled “GET /v1/sites/{siteID}/capabilities”sites:read Ask a site what it can do. Drive your
client off this answer instead of branching on runtime — every runtime reports the
same capability keys, so a static site reports components.plugins as
supported: false while a WordPress site reports the actions it allows.
curl https://api.managed.dev/v1/sites/site_01J7.../capabilities \ -H "Authorization: Bearer mfk_live_..."{ "data": { "runtime": "wordpress", "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 }, "functions": { "supported": false } } }, "request_id": "req_01J9..."}See capability discovery for the “no” semantics
(404 site.not_found when the site doesn’t exist, 409 capability.unsupported when
the route exists but this runtime can’t perform it) and the static
runtime catalog at GET /v1/runtimes.