REST API v1
DeployMill's /api/v1 REST API is an HTTP mirror of the MCP tool surface, letting scripts, CI/CD pipelines, and other HTTP clients drive DeployMill without setting up a full MCP client. Every deploy-lifecycle tool an agent can call over MCP, including mutations like deploy, rollback, env-var changes, object storage, managed database, and backups, is also reachable here. /api/v1 additionally exposes two tools that have no MCP equivalent: create_app (over MCP you create apps through the higher-level start_project / import_repo workflows) and list_projects. A parity test (test/restApi.test.ts) enforces this mirror so a newly added MCP tool can't silently skip the REST surface.
Endpoints
| Endpoint | Notes |
|---|---|
GET /api/v1/openapi.json | OpenAPI 3.1 schema for the full v1 surface. No auth required. |
POST /api/v1/tools/:name | Call a tool by name. Auth required. |
Authentication
POST /api/v1/tools/:name accepts two kinds of bearer credential. Pass either as Authorization: Bearer <token>:
- OAuth 2.0 access token: the same token used for the
POST /mcpendpoint. Best for agents and interactive sessions. Obtain one by running the standard OAuth flow (Dynamic Client Registration + PKCE) against/api/auth/mcp/: - Org-scoped API key (
dm_<hex>): best for CI/CD pipelines and scripts. Created in the dashboard under Organization Settings → API Keys. An org owner must enable API-key access before keys can be created or used. Each key carries a fixed role (member or admin) and never expires unless revoked.
Whichever credential you use, calls are scoped to the org it belongs to, and role-gated tools follow the caller's role (see below).
For local development, bearer local-dev works against a localhost server. Against a preview with E2E_BOOTSTRAP_SECRET set, use that value as the bearer.
Available tools
v1 exposes the full tool surface, grouped by area. The authoritative list (with input shapes and response formats) is always GET /api/v1/openapi.json. The table below is a convenience snapshot.
| Area | Tools |
|---|---|
| Account | get_account, list_templates, search_docs |
| Apps | list_apps, get_app, list_projects, list_deployments, get_logs, debug_app, create_app, deploy, cancel_deploy, rollback, stop_app, start_app, sleep_app, delete_app, set_app_protection, set_app_resources, set_app_sleep_policy |
| Object storage | list_objects, put_object, get_object, delete_object |
| Database | describe_database, query_database, swap_database |
| Backups | list_backups, create_backup, restore_backup, download_backup, verify_backup |
| Domains | list_domains, attach_domain, detach_domain |
| Env vars | list_env_vars, set_env_vars, delete_env_vars |
| Secrets | list_secrets, request_secret, secret_request_status, delete_secret, bind_secret, share_secret, unshare_secret |
| Previews | list_previews, create_preview, set_preview_ttl (redeploy a preview via deploy, and delete one via delete_app, addressing it by (parentApplicationId, ref)) |
| Source | list_files, get_file, push_files, get_clone_credentials |
| Workflows | start_project, import_repo, reconcile_project |
Read tools (list_*, get_*, search_docs) never return secret values: list_env_vars and list_secrets return key names only. Secret values are still entered through the request_secret browser hand-off, and the value never crosses the API client.
A few tools are capability-gated: writing to the secret vault (request_secret, bind_secret, delete_secret — list_secrets only needs secret.read, which members hold by default) and delete_app (including deleting a preview, which requires the preview.delete capability) default to admin (or owner). Gating is by the caller's effective permissions (their role's defaults adjusted by any per-member grants/revokes), not by rank alone, so a member granted secret.write may manage secrets and an admin who's had it revoked may not. A caller lacking the required capability gets a 403. get_account returns the caller's effective permissions array and their memberApps quota (min(org, member)), so an agent can check what it may do and how many apps it may run before acting. See the role matrix for the full breakdown.
Calling a tool
Send a POST with Content-Type: application/json and a JSON body matching the tool's input shape. An empty object {} works for tools with no required inputs.
# List all apps in the authenticated org (read)
curl -s https://your-deploymill.example.com/api/v1/tools/list_apps \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{}'
# Get an app's detail + live health (read)
curl -s https://your-deploymill.example.com/api/v1/tools/get_app \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"applicationId": "app_abc123"}'
# Trigger a deploy of an app's current branch (mutation)
curl -s https://your-deploymill.example.com/api/v1/tools/deploy \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"applicationId": "app_abc123"}'
Error responses
| Status | Meaning |
|---|---|
400 | Business-rule violation, e.g. a quota exceeded or an invalid state. The body's code is the machine-readable reason. |
401 | Missing or invalid bearer credential (OAuth token or dm_ API key) |
403 | The caller's org does not own the referenced resource, or the caller's role is too low for a role-gated tool |
404 | Tool name not found in the v1 registry |
422 | Input validation failed, and the detail field lists the Zod errors |
500 | Unexpected server error |
Mutations and the safety guardrails
Mutating over REST runs the same code path (and the same agent-safety guardrails) as the MCP surface: health-gated deploys with auto-rollback, isolated previews, the secret hand-off the client never sees, and dry-runnable reconcile_project plans. There's no behavioural difference between calling deploy over /api/v1 and over /mcp, so pick whichever transport fits your client.