---
title: "Provision a new VM"
method: POST
path: "/v1/machines"
tags: ["machines"]
---

# Provision a new VM

`POST /v1/machines`

Managed VM provisioning. Managed machines debit the Developer API wallet by runtime state, not agent-minute execution: published defaults are 5 credits/hour running Linux, 9 running Windows, and 1 stopped/suspended, with per-minute granularity. Read effective deployed rates from GET /v1/machines/pricing. Test keys (sk-coasty-test-*) return mock VMs with no cloud calls.

## Headers

- `Idempotency-Key` string

## Request body

- ProvisionRequest
  - `display_name` string, required
  - `os_type` 'linux' | 'windows'
  - `desktop_enabled` boolean
  - `cpu_cores` integer, nullable
  - `memory_gb` integer, nullable
  - `storage_gb` integer, nullable
  - `restore_from_snapshot` boolean, nullable
  - `ttl_minutes` integer, nullable — Optional auto-destroy lease, measured from creation (5 minutes to 7 days).
  - `metadata` object — Max 16 entries; keys ≤64 chars, values ≤256 chars.
  - `proxy` ProxyRequest — Route a machine's outbound traffic through a proxy. 'managed' uses Coasty's residential upstream and takes no other fields; 'custom' requires host and port. Omit the object entirely for no proxy.
    - `mode` 'none' | 'managed' | 'custom'
    - `session` 'sticky' | 'rotating', nullable — Managed mode only. 'sticky' (default) holds one exit IP for the machine's lifetime.
    - `scheme` 'http' | 'https' | 'socks5', nullable — Custom mode only. How to reach YOUR PROXY, not what it can carry: an http proxy serves HTTPS sites fine through CONNECT. Use https only when the proxy itself terminates TLS. Defaults to http.
    - `host` string, nullable — Custom mode only. Must be a public address; loopback, link-local and private ranges are refused.
    - `port` integer, nullable
    - `username` string, nullable
    - `password` string, nullable — Write-only. Encrypted at rest and never returned by any endpoint.

## Response `200`

Machine provisioned (or in-progress).

- ProvisionResponse
  - `machine` MachineRecord, required
    - `proxy` MachineProxyView — Read-side proxy state. Never contains the credential -- a masked username and a fingerprint only.
      - `mode` 'none' | 'managed' | 'custom'
      - `scheme` string, nullable
      - `host` string, nullable
      - `port` integer, nullable
      - `status` 'pending' | 'applying' | 'active' | 'failed' | 'released', nullable
      - `authenticated` boolean, nullable
      - `username_masked` string, nullable
      - `credential_fingerprint` string, nullable
      - `egress_ip` string, nullable — The address the machine was observed leaving from, once proven.
      - `verified_at` string, date-time, nullable
      - `error` string, nullable
    - `id` string, required
    - `display_name` string, required
    - `kind` 'managed' | 'external', required — managed is a Coasty-hosted VM; external is a customer-operated driver that supplies frames and executes typed commands.
    - `capabilities` MachineCapability[], required
    - `protocol_version` string, nullable, required — External-driver protocol version. null for managed machines.
    - `connection_status` 'connected' | 'disconnected' | 'stale' | 'revoked', nullable, required — Liveness of an external driver. null for managed machines; separate from the hosted-VM lifecycle status.
    - `last_seen_at` string, date-time, nullable, required — Most recent authenticated observation, command poll, result, or heartbeat from an external driver.
    - `status` 'creating' | 'provisioning' | 'starting' | 'running' | 'stopping' | 'stopped' | 'restarting' | 'suspended' | 'deleting' | 'terminated' | 'error', required
    - `os_type` 'linux' | 'windows' — Runtime compatibility family. External-driver platform is supplied at enrollment and may be macOS or other even when this compatibility field is linux.
    - `desktop_enabled` boolean
    - `cpu_cores` integer
    - `memory_gb` number
    - `storage_gb` integer
    - `public_ip` string, nullable
    - `is_test` boolean
    - `created_at` string, date-time, nullable
    - `started_at` string, date-time, nullable
    - `auto_destroy_at` string, date-time, nullable
    - `ttl_minutes` integer, nullable
    - `metadata` object
    - `billing` MachineBilling — Per-machine runtime billing state. 1 credit = 1 cent.
      - `billed_to` 'api_wallet'
      - `running_credits_per_hour` integer — Effective API-wallet rate while this machine is running; discover the deployed rate card at GET /v1/machines/pricing.
      - `stopped_credits_per_hour` integer — Effective API-wallet storage rate while stopped/suspended; discover the deployed rate card at GET /v1/machines/pricing.
      - `accrued_cents` integer — Credits owed for the current metering segment so far.
      - `projected_daily_cents` integer — rate_cents_per_hour * 24 — a full day at the current rate.
      - `since` string, date-time, nullable — When the current metering segment started.
      - `total_credits_billed` integer — Lifetime credits this machine has billed.
      - `suspended_for_billing` boolean — true if stopped because the wallet ran dry.
      - `auto_destroy_at` string, date-time, nullable
      - `ttl_minutes` integer, nullable
  - `connection` ConnectionDetailsRedacted, required
    - `public_ip` string, nullable
    - `ssh_port` integer, nullable
    - `ssh_username` string, nullable
    - `vnc_port` integer, nullable
    - `websocket_port` integer, nullable
    - `has_ssh_key` boolean
    - `has_vnc_password` boolean
  - `request_id` string, required

## Other responses

- `400` — Invalid request body or parameters.
- `401` — Missing, invalid, or revoked API key. Pass `X-API-Key: sk-coasty-live-...` (or test).
- `402` — Insufficient balance for this operation. Direct metered API calls and managed-machine runtime use the prepaid Developer API wallet; scheduled execution is the documented consumer subscription-credit exception. Follow the operation's billing fields and top up the matching balance in the dashboard.
- `403` — API key lacks the required scope or tier-feature is unavailable on the caller's plan.
- `404` — Resource not found in this key's namespace.
- `409` — The resource state conflicts with this operation.
- `413` — The request body exceeds the endpoint limit.
- `422` — The JSON shape is valid but one or more values violate the endpoint contract.
- `429` — Rate or concurrency limit exceeded.
- `500` — Unexpected server error. Retry with exponential backoff.
- `502` — An upstream dependency returned an invalid response.
- `503` — A required service is temporarily unavailable.
- `504` — An upstream dependency timed out.

---

[API](https://skmtc.net/coasty/apis/coasty-public-api.md) · [All operations](https://skmtc.net/coasty/apis/coasty-public-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/coasty/coasty-public-api/versions/f50d93b0d8a1/schema)
