Browse docs All docs
Docs / REST API v1
Docdeploymill://docs/rest-api

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

EndpointNotes
GET /api/v1/openapi.jsonOpenAPI 3.1 schema for the full v1 surface. No auth required.
POST /api/v1/tools/:nameCall a tool by name. Auth required.

Authentication

POST /api/v1/tools/:name accepts two kinds of bearer credential. Pass either as Authorization: Bearer <token>:

  1. OAuth 2.0 access token: the same token used for the POST /mcp endpoint. 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.

    AreaTools
    Accountget_account, list_templates, search_docs
    Appslist_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 storagelist_objects, put_object, get_object, delete_object
    Databasedescribe_database, query_database, swap_database
    Backupslist_backups, create_backup, restore_backup, download_backup, verify_backup
    Domainslist_domains, attach_domain, detach_domain
    Env varslist_env_vars, set_env_vars, delete_env_vars
    Secretslist_secrets, request_secret, secret_request_status, delete_secret, bind_secret, share_secret, unshare_secret
    Previewslist_previews, create_preview, set_preview_ttl (redeploy a preview via deploy, and delete one via delete_app, addressing it by (parentApplicationId, ref))
    Sourcelist_files, get_file, push_files, get_clone_credentials
    Workflowsstart_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_secretlist_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

    StatusMeaning
    400Business-rule violation, e.g. a quota exceeded or an invalid state. The body's code is the machine-readable reason.
    401Missing or invalid bearer credential (OAuth token or dm_ API key)
    403The caller's org does not own the referenced resource, or the caller's role is too low for a role-gated tool
    404Tool name not found in the v1 registry
    422Input validation failed, and the detail field lists the Zod errors
    500Unexpected 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.