{"openapi":"3.1.0","info":{"title":"Freestyle","description":"# Powerful Virtual Machines\n\nDocumentation: [freestyle.sh/docs](https://freestyle.sh/docs)\n\nFreestyle VMs are full Linux virtual machines designed for long running, complex tasks.\n\n## Key Features\n\n- **Instant Startup**: Freestyle VMs provision in milliseconds, with p99s under 400ms.\n- **Persistence**: Freestyle VMs can run forever, they can be hibernated with their exact memory in place and come back to life weeks later instantly, in that exact state.\n- **Powerful**: Freestyle VMs go up to 64GB of RAM for public tiers and much further for enterprise. Freestyle does high quality hardware virtualization supporting nested virtualization, FUSE, eBPF, full Linux networking and more. If it can run in an EC2, it can run in a Freestyle VM.\n- **Cheaply Snapshottable and Branchable**: Freestyle VMs are instantly and cheaply snapshottable (sub 1ms interruption, sub 50ms to snapshot ready). These snapshots can be used to create new VMs in the exact state of the original VM (memory and disk). This enables powerful workflows like branching, cloning, and rapid recovery from failures.\n\n## When to use Freestyle\n\n- **Long Running Agent Tasks**: When you have tasks that take hours, days, weeks or months to run, Freestyle VMs provide a persistent and powerful environment that your agent can work in to completion.\n- **Failure Prone/Experimental Exploration**: When your tasks involve branching, cloning or need time travel Freestyle VMs are ideal because of their cheap snapshottability and power that makes them great for experiments.\n- **Complex Coding Problems**: When you are tackling advanced coding problems in research or production codebases, poor virtualization and small sandboxes will not cut it. Freestyle VMs provide the necessary resources and environment to handle these challenges effectively.\n- **Multi Tenant Usage Patterns**: Freestyle VMs are intentionally built to be used by agent builders with multiple customers. The slug system allows for keying VMs by their user, the identity system allows for granular acccess control and permissioning, and the VPC system allows for multi-tenant network isolation. \n\n## When not to use it\n\n- **\"Run AI Code\"**: If your exclusively executing short lived Python scripts or other ephemeral tasks the benefits above aren't relevant. Freestyle VMs should still be benchmarked but there may be alternatives better suited.\n- **CPU Intensive CI/CD**: Freestyle VMs run on bare metal on data center CPUs. We also oversubscribe host resources. For workloads that require sustained high CPU usage like building the Linux Kernel or mining crypto prefer cloud providers that offer consumer CPU performance.\n- **Short Lived Tasks**: If your tasks are extremely short lived and do not benefit from persistence or powerful hardware. If your average task takes less than 15 minutes to complete then they are likely cheaply retryable and don't require the reliability and persistence that Freestyle VMs provide.\n- **Sole Application Hosting**: Freestyle VMs are designed as workbenches for development, experimentation and task execution. Not for hosting production applications. Hosting a minecraft server for some friends will work great on Freestyle, but we do not offer autoscaling, load balancers or CDNs and we don't plan to.\n\n## Emerging Use Cases\n\n- **RL Environments**: Freestyle VMs are well-suited for running reinforcement learning environments that require persistent state and complex setups. The ability to snapshot and branch VMs allows researchers to experiment with different strategies and record environment states efficiently.\n- **Auto Research Climbing**: Freestyle VMs are great for auto research as the snapshotting and fan out allows research agents to explore multiple strategies, review past results and iterate efficiently without ever losing progress.\n- **Environments for Development, Testing, Security and Pentesting**: Freestyle VMs are the only sandbox provider that offers full private VPC and networking which allows simulating full applications in production like staging environments. This can be used for testing, iteration, security assesments and penetration testing in a controlled and isolated environment.\n- **Connecting Agents to Production VPCs**: Freestyle VMs VPC system allows for connections to private production VPCs through secure Wireguard tunnels. If you want sandboxed agents to work on your/your users production infrastructure and they have requirements about not going through the public internet, Freestyle VMs provide a secure and compliant solution.\n\n## Private Offers (Enterprise)\n\n- **GPU Sandboxes**: Freestyle offers private GPU sandboxes for enterprise customers, providing direct access to frontier GPU hardware for high performance computing, machine learning and generally the above **Emerging Use Cases**.\n- **Colocated Bare Metal**: For users wanting sandboxes that work with especially weird requirements Freestyle can offer space in our racks for your dedicated bare metal servers. This can be useful for custom GPUs, video processing, or any other specialized hardware needs.\n- **Custom Pricing**: Freestyle can offer spot access, CPU Time billing, node based pricing and more. These deals can be tailored to the specific needs and usage patterns of enterprise customers.\n- **Custom Sandboxing**: Freestyle builds and maintains its own custom virtualization solutions which allows us to virtualize and sandbox workloads in ways that are not possible with standard options. This enables highly specialized and optimized environments for enterprise customers.\n- **Dedicated Support**: Enterprise customers can receive dedicated support from the Freestyle team, ensuring that any issues or custom requirements are addressed promptly and effectively.\n\nFor more information on Enterprise offerings contact our [sales team](https://cal.com/freestyle/enterprise-onboarding?overlayCalendar=true)\n\n### Relevant Links\n\n- [Freestyle Documentation](https://freestyle.sh/docs)\n- [Freestyle Dashboard](https://dash.freestyle.sh) — manage your account and API keys.\n\nAI agents can create their own API key with `npx -y freestyle@latest tokens create \"agent work\" --output json` using an existing CLI login. Run `npx -y freestyle@latest login` first if needed, then use the returned `token` as `FREESTYLE_API_KEY`.\n","license":{"name":""},"version":"0.1.0"},"paths":{"/background-requests/{requestId}":{"get":{"tags":["Background requests"],"summary":"Poll a background request.","description":"A request started with the `x-freestyle-background-after-secs` header\nanswers `202 Accepted` with a `requestId` once that many seconds elapse;\npoll this route with it. While the request is still running this answers\n`202` again; once it finishes, the request's own response — its status,\nheaders, and body, success or error — is returned exactly as the original\ncall would have. Results expire ten minutes after completion; a result\nthat was delivered inline (the original call returned before the window\nelapsed) is not kept here.","operationId":"get_background_request","parameters":[{"name":"requestId","in":"path","description":"The id from the 202's `requestId` field / `x-freestyle-background-request-id` header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The finished request's own response (any status the original call could produce, replayed verbatim)"},"202":{"description":"Still running; poll again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundRequestPending"}}}},"404":{"description":"Unknown id, expired result, or another account's request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/certificates":{"get":{"tags":["Domains"],"description":"List your certificates.","operationId":"list_certificates","responses":{"200":{"description":"Certificates issued for the account's domains","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCertificatesResponse"}}}}},"deprecated":true}},"/certificates/wildcard":{"post":{"tags":["Domains"],"description":"Request an ongoing `*.domain` certificate. Requires `_acme-challenge.<domain>` to be NS-delegated to Freestyle's nameservers, since wildcards can only be proven over DNS.","operationId":"request_wildcard","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestWildcardRequest"}}},"required":true},"responses":{"200":{"description":"Ongoing wildcard certificate requested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateInfo"}}}},"400":{"description":"INVALID_DOMAIN: not a valid domain name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"403":{"description":"DOMAIN_NOT_OWNED: you have not verified this domain or a parent of it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/domains":{"get":{"tags":["Domains"],"description":"List the domains you hold — those you verified, and any Freestyle subdomain you have taken by publishing it.","operationId":"list_domains","responses":{"200":{"description":"Domains the account holds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDomainsResponse"}}}}},"deprecated":true}},"/domains/placeholder":{"get":{"tags":["Domains"],"description":"The branding your placeholder pages wear. A placeholder page is what a browser sees for a hostname under one of your verified domains that none of your TLS rules serve.","operationId":"get_placeholder_branding","responses":{"200":{"description":"Your placeholder branding","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceholderBranding"}}}},"404":{"description":"NOT_FOUND: no branding set; pages carry the Freestyle default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"put":{"tags":["Domains"],"description":"Set the branding for every placeholder page under your verified domains — including domains you verify later. The page keeps its behaviour (a 404 that reloads itself until a route appears); only the logo and the name change. Replaces any earlier setting.","operationId":"set_placeholder_branding","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPlaceholderBrandingRequest"}}},"required":true},"responses":{"200":{"description":"Branding saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceholderBranding"}}}},"400":{"description":"INVALID_REQUEST: the logo URL is not an absolute https URL, or a field is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Domains"],"description":"Remove your placeholder branding. Pages go back to the Freestyle default.","operationId":"delete_placeholder_branding","responses":{"204":{"description":"Branding removed"},"404":{"description":"NOT_FOUND: no branding was set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/firewall/evaluate":{"post":{"tags":["Firewall"],"description":"Ask whether a connection would be allowed by your rules, without changing anything. Useful for checking a rule set before you rely on it. Traffic Freestyle itself delivers — a mapped domain, the SSH proxy — answers `allowedByPlatform`: it is allowed whatever your rules say, and no rule can block it. Outbound mail answers `deniedByPlatform`: a connection to a mail port on the public Internet is closed whatever your rules say, and goes through an `smtp` TLS rule instead.","operationId":"evaluate_firewall","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrafficDescription"}}},"required":true},"responses":{"200":{"description":"The decision, and what produced it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirewallDecision"}}}},"400":{"description":"BAD_REQUEST: the traffic description is not well formed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/firewall/rules":{"get":{"tags":["Firewall"],"description":"List your firewall rules, newest first. Filter by `vmId` to get the rules that apply to one VM — those naming it, plus those naming a private network it is attached to — or by `vpcId`/`tunnelId` for the rules naming one of those.","operationId":"list_firewall_rules","parameters":[{"name":"vmId","in":"query","description":"Rules that apply to this VM: those naming it, plus those naming a\nprivate network it is attached to. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"vpcId","in":"query","description":"Rules naming this private network. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"tunnelId","in":"query","description":"Rules naming this tunnel, by its id.","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's firewall rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListFirewallRulesResponse"}}}},"404":{"description":"NOT_FOUND: a `vmId`, `vpcId`, or `tunnelId` filter names something that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"post":{"tags":["Firewall"],"description":"Allow traffic from one place to another. Both `source` and `destination` are matchers whose fields intersect; use `public: true` for the public Internet. A rule naming a VM, a private network, or a tunnel is deleted automatically when that resource is.","operationId":"create_firewall_rule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFirewallRuleRequest"}}},"required":true},"responses":{"200":{"description":"Rule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirewallRule"}}}},"400":{"description":"BAD_REQUEST: an unknown field, a matcher that identifies nothing, a port without a protocol, or a malformed CIDR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: the rule names a VM, network, or tunnel that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: your account is at its firewall rule limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/firewall/rules/{ruleId}":{"get":{"tags":["Firewall"],"description":"Fetch one firewall rule.","operationId":"get_firewall_rule","parameters":[{"name":"ruleId","in":"path","description":"Firewall rule id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirewallRule"}}}},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Firewall"],"description":"Delete a firewall rule. The resources it named are untouched — the dependency runs one way.","operationId":"delete_firewall_rule","parameters":[{"name":"ruleId","in":"path","description":"Firewall rule id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Rule deleted"},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/hardware":{"get":{"tags":["Hardware"],"description":"List the physical machines attached to your private networks.","operationId":"list_hardware","responses":{"200":{"description":"Your physical machines","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListHardwareResponse"}}}}},"deprecated":true}},"/hardware/{hardwareId}/power":{"get":{"tags":["Hardware"],"description":"Read a machine's power: whether every cord is live, what it is drawing, and how much of its rate limit is left. Reading is free and never limited.","operationId":"get_hardware_power","parameters":[{"name":"hardwareId","in":"path","description":"Hardware machine id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The machine's power","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardwarePowerState"}}}},"404":{"description":"NOT_FOUND: no such machine in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"post":{"tags":["Hardware"],"description":"Switch a physical machine's power.\n\nThis is a real power outlet, so it is deliberately narrow. The unit is the whole machine: machines with redundant supplies draw from several cords and all of them are switched together, because cutting one browns the machine out instead of powering it off. `cycle` cuts power, holds a few seconds, and restores it — anything running stops immediately, with no chance to shut down cleanly.\n\nHeavily rate limited: one power-cutting action every few minutes and a small daily budget per machine, both reported by `GET /hardware/{hardwareId}/power`. Turning power back **on** is never rationed, only briefly debounced. Exceeding a limit is a 429 whose message says when to come back.","operationId":"set_hardware_power","parameters":[{"name":"hardwareId","in":"path","description":"Hardware machine id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardwarePowerRequest"}}},"required":true},"responses":{"200":{"description":"The action was applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardwarePowerOutcome"}}}},"400":{"description":"BAD_REQUEST: power control is not enabled for this machine","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such machine in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"429":{"description":"RATE_LIMITED: too soon, or the daily budget is spent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"503":{"description":"UNAVAILABLE: the machine's power could not be reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/identities":{"get":{"tags":["Identities"],"description":"List your identities.","operationId":"list_identities","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"includeManaged","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"The caller account's identities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListIdentities"}}}},"401":{"description":"MISSING_ACCOUNT_CONTEXT: no account resolved for this credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"post":{"tags":["Identities"],"description":"Create an identity.","operationId":"create_identity","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIdentityBody"}}},"required":true},"responses":{"200":{"description":"Identity created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreestyleIdentity"}}}},"401":{"description":"MISSING_ACCOUNT_CONTEXT: no account resolved for this credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/identities/{identity_id}":{"get":{"tags":["Identities"],"description":"Fetch an identity.","operationId":"describe_identity","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"Identity detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreestyleIdentityInfo"}}}},"404":{"description":"IDENTITY_NOT_FOUND: no such identity in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Identities"],"description":"Delete an identity, along with its tokens and grants.","operationId":"delete_identity","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"204":{"description":"Identity deleted"},"400":{"description":"MANAGED_IDENTITY_DELETE: managed identities cannot be deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/identities/{identity_id}/permissions/vm":{"get":{"tags":["Identities"],"description":"List the VMs an identity has been granted access to.","operationId":"list_vm_permissions","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"The identity's VM permission grants","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VmPermission"}}}}}},"deprecated":true},"post":{"tags":["Identities"],"description":"Grant an identity access to a VM.","operationId":"grant_vm_permission","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantVmPermissionBody"}}},"required":true},"responses":{"200":{"description":"VM permission granted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPermission"}}}},"401":{"description":"MISSING_ACCOUNT_CONTEXT: no account resolved for this credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/identities/{identity_id}/permissions/vm/{vm_id}":{"get":{"tags":["Identities"],"description":"Fetch an identity's grant on a VM.","operationId":"describe_vm_permission","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"vm_id","in":"path","description":"VM id","required":true,"schema":{"$ref":"#/components/schemas/VmId"}}],"responses":{"200":{"description":"VM permission detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPermission"}}}},"404":{"description":"VM_PERMISSION_NOT_FOUND: that identity has no grant on that VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Identities"],"description":"Revoke an identity's access to a VM.","operationId":"revoke_vm_permission","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"vm_id","in":"path","description":"VM id","required":true,"schema":{"$ref":"#/components/schemas/VmId"}}],"responses":{"204":{"description":"VM permission revoked"}},"deprecated":true},"patch":{"tags":["Identities"],"description":"Change which Linux users an identity may act as on a VM.","operationId":"update_vm_allowed_linux_users","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"vm_id","in":"path","description":"VM id","required":true,"schema":{"$ref":"#/components/schemas/VmId"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAllowedLinuxUsersBody"}}},"required":true},"responses":{"200":{"description":"Allowed Linux users updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPermission"}}}},"404":{"description":"VM_PERMISSION_NOT_FOUND: that identity has no grant on that VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/identities/{identity_id}/tokens":{"get":{"tags":["Identities"],"description":"List an identity's access tokens.","operationId":"list_access_tokens","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"The identity's access tokens (ids only)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccessTokenInfo"}}}}}},"deprecated":true},"post":{"tags":["Identities"],"description":"Mint an access token for an identity.","operationId":"create_access_token","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"Access token created (returned once, in full)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedToken"}}}}},"deprecated":true}},"/identities/{identity_id}/tokens/{token_id}":{"delete":{"tags":["Identities"],"description":"Revoke an identity's access token.","operationId":"revoke_access_token","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"token_id","in":"path","description":"Access token id","required":true,"schema":{"$ref":"#/components/schemas/IdentityTokenId"}}],"responses":{"204":{"description":"Access token revoked"}},"deprecated":true}},"/snapshots":{"get":{"tags":["Snapshots"],"description":"List your snapshots.","operationId":"list_snapshots","parameters":[{"name":"sourceVmId","in":"query","description":"Only snapshots taken from this VM.","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSnapshotsResponse"}}}}},"deprecated":true}},"/snapshots/{snapshotIdOrSlug}":{"get":{"tags":["Snapshots"],"description":"Fetch a snapshot by its id or its slug.","operationId":"get_snapshot","parameters":[{"name":"snapshotIdOrSlug","in":"path","description":"Snapshot id, your slug, or `{owner}/{slug}` for a public one","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Snapshot"}}}},"404":{"description":"NOT_FOUND: no such snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Snapshots"],"description":"Delete a snapshot permanently. VMs already created from it are unaffected — a snapshot is a pointer to the data backing them, not the data itself — and its slug is freed immediately.","operationId":"delete_snapshot","parameters":[{"name":"snapshotIdOrSlug","in":"path","description":"Snapshot id or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Snapshot deleted"},"404":{"description":"NOT_FOUND: no such snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the snapshot is the platform default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"patch":{"tags":["Snapshots"],"description":"Rename a snapshot or change its slug.","operationId":"update_snapshot","parameters":[{"name":"snapshotIdOrSlug","in":"path","description":"Snapshot id or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSnapshotRequest"}}},"required":true},"responses":{"200":{"description":"The updated snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Snapshot"}}}},"400":{"description":"BAD_REQUEST: invalid slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another snapshot in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tls":{"get":{"tags":["TLS"],"description":"List your TLS rules, newest first. Filter by `vmId` to get the rules that apply to one VM — those naming it, plus those naming a private network it is attached to — by `vpcId` for the rules naming a private network, or by `domain` for the rules written on exactly that name.","operationId":"list_tls_rules","parameters":[{"name":"vmId","in":"query","description":"Rules that apply to this VM: those naming it, plus those naming a private\nnetwork it is attached to. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"vpcId","in":"query","description":"Rules naming this private network. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"domain","in":"query","description":"Rules for exactly this domain, as written on the rule (e.g.\n`app.acme.com` or `*.acme.com`).","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's TLS rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTlsRulesResponse"}}}},"404":{"description":"NOT_FOUND: a `vmId` or `vpcId` filter names something that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"post":{"tags":["TLS"],"description":"Allow a named session over a domain: a session addressed to `domain`, from `source`, to `destination`. A `source` says who may open it (`vmId`/`vpcId`/`public: true`); a `destination` says where it lands (`vmId`+`port`, `host`+`port`, or `public: true` for the domain's own origin). A `transform` rewrites the session in flight — setting a header, or completing a Postgres handshake with credentials the guest never holds; its secrets are write-only and read back as `\"***\"`. A rule naming a VM or private network is deleted automatically when that resource is.","operationId":"create_tls_rule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsRuleRequest"}}},"required":true},"responses":{"200":{"description":"Rule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsRule"}}}},"400":{"description":"BAD_REQUEST: an unknown field, an endpoint that identifies nothing, a landing without a port, or a transform that disagrees with the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: the rule names a VM or network that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: your account is at its TLS rule limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tls/forward-auth":{"get":{"tags":["TLS"],"description":"List your reusable HTTP authorization checks, newest first.","operationId":"list_tls_forward_auth","responses":{"200":{"description":"The account's forward-auth configurations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTlsForwardAuthResponse"}}}}},"deprecated":true},"post":{"tags":["TLS"],"description":"Create a reusable authorization check for public HTTP ingress. Freestyle sends a bodyless GET to the HTTPS endpoint before waking or forwarding to the VM. Static header values are write-only and sealed at rest.","operationId":"create_tls_forward_auth","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsForwardAuthRequest"}}},"required":true},"responses":{"200":{"description":"Configuration created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsForwardAuth"}}}},"400":{"description":"BAD_REQUEST: the URL, timeout, header, or cookie configuration is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: your account is at its forward-auth configuration limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"503":{"description":"UNAVAILABLE: secret sealing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tls/forward-auth/{forwardAuthId}":{"get":{"tags":["TLS"],"description":"Fetch one reusable authorization check. Static header values read back as `\"***\"`.","operationId":"get_tls_forward_auth","parameters":[{"name":"forwardAuthId","in":"path","description":"forwardAuth configuration id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsForwardAuth"}}}},"404":{"description":"NOT_FOUND: no such configuration in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"put":{"tags":["TLS"],"description":"Replace a reusable authorization check in place, including rotating its static headers. Rules keep referencing the same id.","operationId":"update_tls_forward_auth","parameters":[{"name":"forwardAuthId","in":"path","description":"forwardAuth configuration id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsForwardAuthRequest"}}},"required":true},"responses":{"200":{"description":"Configuration replaced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsForwardAuth"}}}},"400":{"description":"BAD_REQUEST: the replacement is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such configuration in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"503":{"description":"UNAVAILABLE: secret sealing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["TLS"],"description":"Delete an unused authorization check. A configuration referenced by a TLS rule cannot be deleted.","operationId":"delete_tls_forward_auth","parameters":[{"name":"forwardAuthId","in":"path","description":"forwardAuth configuration id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Configuration deleted"},"404":{"description":"NOT_FOUND: no such configuration in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: one or more TLS rules still reference the configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tls/{ruleId}":{"get":{"tags":["TLS"],"description":"Fetch one TLS rule. Transform secrets read back as `\"***\"`.","operationId":"get_tls_rule","parameters":[{"name":"ruleId","in":"path","description":"TLS rule id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsRule"}}}},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"put":{"tags":["TLS"],"description":"Replace a TLS rule in place — the one mutation this API allows, so a rotating secret can change without dropping traffic. The body is a whole rule, exactly as create takes it.","operationId":"update_tls_rule","parameters":[{"name":"ruleId","in":"path","description":"TLS rule id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsRuleRequest"}}},"required":true},"responses":{"200":{"description":"Rule replaced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsRule"}}}},"400":{"description":"BAD_REQUEST: the replacement rule is not well formed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["TLS"],"description":"Delete a TLS rule. The resources it named are untouched — the dependency runs one way.","operationId":"delete_tls_rule","parameters":[{"name":"ruleId","in":"path","description":"TLS rule id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Rule deleted"},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tunnels":{"get":{"tags":["Tunnels"],"description":"List your tunnels, newest first. Listings never include a private key: one is returned only by create and rotate-key.","operationId":"list_tunnels","responses":{"200":{"description":"Your tunnels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTunnelsResponse"}}}}},"deprecated":true},"post":{"tags":["Tunnels"],"description":"Create a tunnel, optionally attaching networks in the same call (`vpcs`, all-or-nothing). It lives until you delete it, and its config never changes: save it, bring it up once, and manage which networks it reaches by attaching and detaching them — no config updates, ever. Omit `clientPublicKey` and a keypair is minted for you — the response carries the only copy of the private key you will get.","operationId":"create_tunnel","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTunnelRequest"}}},"required":true},"responses":{"200":{"description":"Tunnel ready, with any requested networks attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTunnel"}}}},"400":{"description":"BAD_REQUEST: invalid slug, display name, public key, routes, or address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: a named network does not exist in this account; nothing was created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another tunnel in your account, or a requested attachment was refused; nothing was created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tunnels/{tunnelIdOrSlug}":{"get":{"tags":["Tunnels"],"description":"Read one tunnel, with its attached networks. The config comes back with a blank `PrivateKey`; fill in the key you saved at create, or rotate to mint a new one.","operationId":"get_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Tunnels"],"description":"Delete a tunnel, detaching every network as it goes. This is the only thing that ends one.","operationId":"delete_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Tunnel deleted"},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"patch":{"tags":["Tunnels"],"description":"Rename a tunnel or change its slug. Only the labels move: the keys, addresses, routes and attached networks are untouched, so a connected client sees nothing change and the config you saved keeps working.","operationId":"update_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTunnelRequest"}}},"required":true},"responses":{"200":{"description":"The updated tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"400":{"description":"BAD_REQUEST: invalid slug or display name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another tunnel in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tunnels/{tunnelIdOrSlug}/rotate-key":{"post":{"tags":["Tunnels"],"description":"Replace a tunnel's keys, keeping its id and every attached network's address. Use this if you lost the config or need to revoke one that leaked: the old keys stop working, and the response carries the only copy of the new private key you will get.","operationId":"rotate_tunnel_key","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateTunnelKeyRequest"}}},"required":true},"responses":{"200":{"description":"Keys rotated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTunnel"}}}},"400":{"description":"BAD_REQUEST: that is not a valid WireGuard public key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/tunnels/{tunnelIdOrSlug}/vpcs/{vpcIdOrSlug}":{"post":{"tags":["Tunnels"],"description":"Attach a private network to a tunnel. The tunnel gains an address inside the network — what that network's VMs see as your address — and a connected client can reach the network immediately, with no config change. Omit `ipv4` and an address is assigned; supply one to pin it. The network's CIDRs must fall inside the tunnel's routes and must not overlap another attached network's.","operationId":"attach_vpc_to_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachVpcRequest"}}},"required":true},"responses":{"200":{"description":"Network attached; the updated tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"400":{"description":"BAD_REQUEST: invalid address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such tunnel or network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: already attached, that address is already taken, the network's CIDRs overlap an already-attached network's, or they fall outside the tunnel's routes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Tunnels"],"description":"Detach a private network from a tunnel: it stops being reachable and the tunnel's address inside it is released. The tunnel — and any connected client — carries on untouched.","operationId":"detach_vpc_from_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Network detached; the updated tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"404":{"description":"NOT_FOUND: no such tunnel, or the network is not attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/v5/background-requests/{requestId}":{"get":{"tags":["Background requests"],"summary":"Poll a background request.","description":"A request started with the `x-freestyle-background-after-secs` header\nanswers `202 Accepted` with a `requestId` once that many seconds elapse;\npoll this route with it. While the request is still running this answers\n`202` again; once it finishes, the request's own response — its status,\nheaders, and body, success or error — is returned exactly as the original\ncall would have. Results expire ten minutes after completion; a result\nthat was delivered inline (the original call returned before the window\nelapsed) is not kept here.","operationId":"get_background_request","parameters":[{"name":"requestId","in":"path","description":"The id from the 202's `requestId` field / `x-freestyle-background-request-id` header","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The finished request's own response (any status the original call could produce, replayed verbatim)"},"202":{"description":"Still running; poll again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundRequestPending"}}}},"404":{"description":"Unknown id, expired result, or another account's request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/certificates":{"get":{"tags":["Domains"],"description":"List your certificates.","operationId":"list_certificates","responses":{"200":{"description":"Certificates issued for the account's domains","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCertificatesResponse"}}}}}}},"/v5/certificates/wildcard":{"post":{"tags":["Domains"],"description":"Request an ongoing `*.domain` certificate. Requires `_acme-challenge.<domain>` to be NS-delegated to Freestyle's nameservers, since wildcards can only be proven over DNS.","operationId":"request_wildcard","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestWildcardRequest"}}},"required":true},"responses":{"200":{"description":"Ongoing wildcard certificate requested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CertificateInfo"}}}},"400":{"description":"INVALID_DOMAIN: not a valid domain name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"403":{"description":"DOMAIN_NOT_OWNED: you have not verified this domain or a parent of it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/domains":{"get":{"tags":["Domains"],"description":"List the domains you hold — those you verified, and any Freestyle subdomain you have taken by publishing it.","operationId":"list_domains","responses":{"200":{"description":"Domains the account holds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDomainsResponse"}}}}}}},"/v5/domains/placeholder":{"get":{"tags":["Domains"],"description":"The branding your placeholder pages wear. A placeholder page is what a browser sees for a hostname under one of your verified domains that none of your TLS rules serve.","operationId":"get_placeholder_branding","responses":{"200":{"description":"Your placeholder branding","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceholderBranding"}}}},"404":{"description":"NOT_FOUND: no branding set; pages carry the Freestyle default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"put":{"tags":["Domains"],"description":"Set the branding for every placeholder page under your verified domains — including domains you verify later. The page keeps its behaviour (a 404 that reloads itself until a route appears); only the logo and the name change. Replaces any earlier setting.","operationId":"set_placeholder_branding","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPlaceholderBrandingRequest"}}},"required":true},"responses":{"200":{"description":"Branding saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceholderBranding"}}}},"400":{"description":"INVALID_REQUEST: the logo URL is not an absolute https URL, or a field is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Domains"],"description":"Remove your placeholder branding. Pages go back to the Freestyle default.","operationId":"delete_placeholder_branding","responses":{"204":{"description":"Branding removed"},"404":{"description":"NOT_FOUND: no branding was set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/firewall/evaluate":{"post":{"tags":["Firewall"],"description":"Ask whether a connection would be allowed by your rules, without changing anything. Useful for checking a rule set before you rely on it. Traffic Freestyle itself delivers — a mapped domain, the SSH proxy — answers `allowedByPlatform`: it is allowed whatever your rules say, and no rule can block it. Outbound mail answers `deniedByPlatform`: a connection to a mail port on the public Internet is closed whatever your rules say, and goes through an `smtp` TLS rule instead.","operationId":"evaluate_firewall","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrafficDescription"}}},"required":true},"responses":{"200":{"description":"The decision, and what produced it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirewallDecision"}}}},"400":{"description":"BAD_REQUEST: the traffic description is not well formed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/firewall/rules":{"get":{"tags":["Firewall"],"description":"List your firewall rules, newest first. Filter by `vmId` to get the rules that apply to one VM — those naming it, plus those naming a private network it is attached to — or by `vpcId`/`tunnelId` for the rules naming one of those.","operationId":"list_firewall_rules","parameters":[{"name":"vmId","in":"query","description":"Rules that apply to this VM: those naming it, plus those naming a\nprivate network it is attached to. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"vpcId","in":"query","description":"Rules naming this private network. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"tunnelId","in":"query","description":"Rules naming this tunnel, by its id.","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's firewall rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListFirewallRulesResponse"}}}},"404":{"description":"NOT_FOUND: a `vmId`, `vpcId`, or `tunnelId` filter names something that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"post":{"tags":["Firewall"],"description":"Allow traffic from one place to another. Both `source` and `destination` are matchers whose fields intersect; use `public: true` for the public Internet. A rule naming a VM, a private network, or a tunnel is deleted automatically when that resource is.","operationId":"create_firewall_rule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFirewallRuleRequest"}}},"required":true},"responses":{"200":{"description":"Rule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirewallRule"}}}},"400":{"description":"BAD_REQUEST: an unknown field, a matcher that identifies nothing, a port without a protocol, or a malformed CIDR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: the rule names a VM, network, or tunnel that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: your account is at its firewall rule limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/firewall/rules/{ruleId}":{"get":{"tags":["Firewall"],"description":"Fetch one firewall rule.","operationId":"get_firewall_rule","parameters":[{"name":"ruleId","in":"path","description":"Firewall rule id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirewallRule"}}}},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Firewall"],"description":"Delete a firewall rule. The resources it named are untouched — the dependency runs one way.","operationId":"delete_firewall_rule","parameters":[{"name":"ruleId","in":"path","description":"Firewall rule id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Rule deleted"},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/hardware":{"get":{"tags":["Hardware"],"description":"List the physical machines attached to your private networks.","operationId":"list_hardware","responses":{"200":{"description":"Your physical machines","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListHardwareResponse"}}}}}}},"/v5/hardware/{hardwareId}/power":{"get":{"tags":["Hardware"],"description":"Read a machine's power: whether every cord is live, what it is drawing, and how much of its rate limit is left. Reading is free and never limited.","operationId":"get_hardware_power","parameters":[{"name":"hardwareId","in":"path","description":"Hardware machine id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The machine's power","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardwarePowerState"}}}},"404":{"description":"NOT_FOUND: no such machine in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"post":{"tags":["Hardware"],"description":"Switch a physical machine's power.\n\nThis is a real power outlet, so it is deliberately narrow. The unit is the whole machine: machines with redundant supplies draw from several cords and all of them are switched together, because cutting one browns the machine out instead of powering it off. `cycle` cuts power, holds a few seconds, and restores it — anything running stops immediately, with no chance to shut down cleanly.\n\nHeavily rate limited: one power-cutting action every few minutes and a small daily budget per machine, both reported by `GET /hardware/{hardwareId}/power`. Turning power back **on** is never rationed, only briefly debounced. Exceeding a limit is a 429 whose message says when to come back.","operationId":"set_hardware_power","parameters":[{"name":"hardwareId","in":"path","description":"Hardware machine id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardwarePowerRequest"}}},"required":true},"responses":{"200":{"description":"The action was applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardwarePowerOutcome"}}}},"400":{"description":"BAD_REQUEST: power control is not enabled for this machine","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such machine in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"429":{"description":"RATE_LIMITED: too soon, or the daily budget is spent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"503":{"description":"UNAVAILABLE: the machine's power could not be reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/identities":{"get":{"tags":["Identities"],"description":"List your identities.","operationId":"list_identities","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"includeManaged","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"The caller account's identities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListIdentities"}}}},"401":{"description":"MISSING_ACCOUNT_CONTEXT: no account resolved for this credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"post":{"tags":["Identities"],"description":"Create an identity.","operationId":"create_identity","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIdentityBody"}}},"required":true},"responses":{"200":{"description":"Identity created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreestyleIdentity"}}}},"401":{"description":"MISSING_ACCOUNT_CONTEXT: no account resolved for this credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/identities/{identity_id}":{"get":{"tags":["Identities"],"description":"Fetch an identity.","operationId":"describe_identity","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"Identity detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreestyleIdentityInfo"}}}},"404":{"description":"IDENTITY_NOT_FOUND: no such identity in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Identities"],"description":"Delete an identity, along with its tokens and grants.","operationId":"delete_identity","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"204":{"description":"Identity deleted"},"400":{"description":"MANAGED_IDENTITY_DELETE: managed identities cannot be deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/identities/{identity_id}/permissions/vm":{"get":{"tags":["Identities"],"description":"List the VMs an identity has been granted access to.","operationId":"list_vm_permissions","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"The identity's VM permission grants","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VmPermission"}}}}}}},"post":{"tags":["Identities"],"description":"Grant an identity access to a VM.","operationId":"grant_vm_permission","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantVmPermissionBody"}}},"required":true},"responses":{"200":{"description":"VM permission granted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPermission"}}}},"401":{"description":"MISSING_ACCOUNT_CONTEXT: no account resolved for this credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/identities/{identity_id}/permissions/vm/{vm_id}":{"get":{"tags":["Identities"],"description":"Fetch an identity's grant on a VM.","operationId":"describe_vm_permission","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"vm_id","in":"path","description":"VM id","required":true,"schema":{"$ref":"#/components/schemas/VmId"}}],"responses":{"200":{"description":"VM permission detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPermission"}}}},"404":{"description":"VM_PERMISSION_NOT_FOUND: that identity has no grant on that VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Identities"],"description":"Revoke an identity's access to a VM.","operationId":"revoke_vm_permission","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"vm_id","in":"path","description":"VM id","required":true,"schema":{"$ref":"#/components/schemas/VmId"}}],"responses":{"204":{"description":"VM permission revoked"}}},"patch":{"tags":["Identities"],"description":"Change which Linux users an identity may act as on a VM.","operationId":"update_vm_allowed_linux_users","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"vm_id","in":"path","description":"VM id","required":true,"schema":{"$ref":"#/components/schemas/VmId"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAllowedLinuxUsersBody"}}},"required":true},"responses":{"200":{"description":"Allowed Linux users updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPermission"}}}},"404":{"description":"VM_PERMISSION_NOT_FOUND: that identity has no grant on that VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/identities/{identity_id}/tokens":{"get":{"tags":["Identities"],"description":"List an identity's access tokens.","operationId":"list_access_tokens","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"The identity's access tokens (ids only)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccessTokenInfo"}}}}}}},"post":{"tags":["Identities"],"description":"Mint an access token for an identity.","operationId":"create_access_token","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}}],"responses":{"200":{"description":"Access token created (returned once, in full)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedToken"}}}}}}},"/v5/identities/{identity_id}/tokens/{token_id}":{"delete":{"tags":["Identities"],"description":"Revoke an identity's access token.","operationId":"revoke_access_token","parameters":[{"name":"identity_id","in":"path","description":"Identity id","required":true,"schema":{"$ref":"#/components/schemas/IdentityId"}},{"name":"token_id","in":"path","description":"Access token id","required":true,"schema":{"$ref":"#/components/schemas/IdentityTokenId"}}],"responses":{"204":{"description":"Access token revoked"}}}},"/v5/snapshots":{"get":{"tags":["Snapshots"],"description":"List your snapshots.","operationId":"list_snapshots","parameters":[{"name":"sourceVmId","in":"query","description":"Only snapshots taken from this VM.","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSnapshotsResponse"}}}}}}},"/v5/snapshots/{snapshotIdOrSlug}":{"get":{"tags":["Snapshots"],"description":"Fetch a snapshot by its id or its slug.","operationId":"get_snapshot","parameters":[{"name":"snapshotIdOrSlug","in":"path","description":"Snapshot id, your slug, or `{owner}/{slug}` for a public one","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Snapshot"}}}},"404":{"description":"NOT_FOUND: no such snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Snapshots"],"description":"Delete a snapshot permanently. VMs already created from it are unaffected — a snapshot is a pointer to the data backing them, not the data itself — and its slug is freed immediately.","operationId":"delete_snapshot","parameters":[{"name":"snapshotIdOrSlug","in":"path","description":"Snapshot id or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Snapshot deleted"},"404":{"description":"NOT_FOUND: no such snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the snapshot is the platform default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"patch":{"tags":["Snapshots"],"description":"Rename a snapshot or change its slug.","operationId":"update_snapshot","parameters":[{"name":"snapshotIdOrSlug","in":"path","description":"Snapshot id or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSnapshotRequest"}}},"required":true},"responses":{"200":{"description":"The updated snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Snapshot"}}}},"400":{"description":"BAD_REQUEST: invalid slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another snapshot in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tls":{"get":{"tags":["TLS"],"description":"List your TLS rules, newest first. Filter by `vmId` to get the rules that apply to one VM — those naming it, plus those naming a private network it is attached to — by `vpcId` for the rules naming a private network, or by `domain` for the rules written on exactly that name.","operationId":"list_tls_rules","parameters":[{"name":"vmId","in":"query","description":"Rules that apply to this VM: those naming it, plus those naming a private\nnetwork it is attached to. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"vpcId","in":"query","description":"Rules naming this private network. Its id, or your slug for it.","required":false,"schema":{"type":"string"}},{"name":"domain","in":"query","description":"Rules for exactly this domain, as written on the rule (e.g.\n`app.acme.com` or `*.acme.com`).","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's TLS rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTlsRulesResponse"}}}},"404":{"description":"NOT_FOUND: a `vmId` or `vpcId` filter names something that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"post":{"tags":["TLS"],"description":"Allow a named session over a domain: a session addressed to `domain`, from `source`, to `destination`. A `source` says who may open it (`vmId`/`vpcId`/`public: true`); a `destination` says where it lands (`vmId`+`port`, `host`+`port`, or `public: true` for the domain's own origin). A `transform` rewrites the session in flight — setting a header, or completing a Postgres handshake with credentials the guest never holds; its secrets are write-only and read back as `\"***\"`. A rule naming a VM or private network is deleted automatically when that resource is.","operationId":"create_tls_rule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsRuleRequest"}}},"required":true},"responses":{"200":{"description":"Rule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsRule"}}}},"400":{"description":"BAD_REQUEST: an unknown field, an endpoint that identifies nothing, a landing without a port, or a transform that disagrees with the protocol","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: the rule names a VM or network that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: your account is at its TLS rule limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tls/forward-auth":{"get":{"tags":["TLS"],"description":"List your reusable HTTP authorization checks, newest first.","operationId":"list_tls_forward_auth","responses":{"200":{"description":"The account's forward-auth configurations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTlsForwardAuthResponse"}}}}}},"post":{"tags":["TLS"],"description":"Create a reusable authorization check for public HTTP ingress. Freestyle sends a bodyless GET to the HTTPS endpoint before waking or forwarding to the VM. Static header values are write-only and sealed at rest.","operationId":"create_tls_forward_auth","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsForwardAuthRequest"}}},"required":true},"responses":{"200":{"description":"Configuration created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsForwardAuth"}}}},"400":{"description":"BAD_REQUEST: the URL, timeout, header, or cookie configuration is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: your account is at its forward-auth configuration limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"503":{"description":"UNAVAILABLE: secret sealing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tls/forward-auth/{forwardAuthId}":{"get":{"tags":["TLS"],"description":"Fetch one reusable authorization check. Static header values read back as `\"***\"`.","operationId":"get_tls_forward_auth","parameters":[{"name":"forwardAuthId","in":"path","description":"forwardAuth configuration id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsForwardAuth"}}}},"404":{"description":"NOT_FOUND: no such configuration in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"put":{"tags":["TLS"],"description":"Replace a reusable authorization check in place, including rotating its static headers. Rules keep referencing the same id.","operationId":"update_tls_forward_auth","parameters":[{"name":"forwardAuthId","in":"path","description":"forwardAuth configuration id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsForwardAuthRequest"}}},"required":true},"responses":{"200":{"description":"Configuration replaced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsForwardAuth"}}}},"400":{"description":"BAD_REQUEST: the replacement is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such configuration in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"503":{"description":"UNAVAILABLE: secret sealing is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["TLS"],"description":"Delete an unused authorization check. A configuration referenced by a TLS rule cannot be deleted.","operationId":"delete_tls_forward_auth","parameters":[{"name":"forwardAuthId","in":"path","description":"forwardAuth configuration id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Configuration deleted"},"404":{"description":"NOT_FOUND: no such configuration in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: one or more TLS rules still reference the configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tls/{ruleId}":{"get":{"tags":["TLS"],"description":"Fetch one TLS rule. Transform secrets read back as `\"***\"`.","operationId":"get_tls_rule","parameters":[{"name":"ruleId","in":"path","description":"TLS rule id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsRule"}}}},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"put":{"tags":["TLS"],"description":"Replace a TLS rule in place — the one mutation this API allows, so a rotating secret can change without dropping traffic. The body is a whole rule, exactly as create takes it.","operationId":"update_tls_rule","parameters":[{"name":"ruleId","in":"path","description":"TLS rule id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTlsRuleRequest"}}},"required":true},"responses":{"200":{"description":"Rule replaced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TlsRule"}}}},"400":{"description":"BAD_REQUEST: the replacement rule is not well formed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["TLS"],"description":"Delete a TLS rule. The resources it named are untouched — the dependency runs one way.","operationId":"delete_tls_rule","parameters":[{"name":"ruleId","in":"path","description":"TLS rule id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Rule deleted"},"404":{"description":"NOT_FOUND: no such rule in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tunnels":{"get":{"tags":["Tunnels"],"description":"List your tunnels, newest first. Listings never include a private key: one is returned only by create and rotate-key.","operationId":"list_tunnels","responses":{"200":{"description":"Your tunnels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTunnelsResponse"}}}}}},"post":{"tags":["Tunnels"],"description":"Create a tunnel, optionally attaching networks in the same call (`vpcs`, all-or-nothing). It lives until you delete it, and its config never changes: save it, bring it up once, and manage which networks it reaches by attaching and detaching them — no config updates, ever. Omit `clientPublicKey` and a keypair is minted for you — the response carries the only copy of the private key you will get.","operationId":"create_tunnel","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTunnelRequest"}}},"required":true},"responses":{"200":{"description":"Tunnel ready, with any requested networks attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTunnel"}}}},"400":{"description":"BAD_REQUEST: invalid slug, display name, public key, routes, or address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: a named network does not exist in this account; nothing was created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another tunnel in your account, or a requested attachment was refused; nothing was created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tunnels/{tunnelIdOrSlug}":{"get":{"tags":["Tunnels"],"description":"Read one tunnel, with its attached networks. The config comes back with a blank `PrivateKey`; fill in the key you saved at create, or rotate to mint a new one.","operationId":"get_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Tunnels"],"description":"Delete a tunnel, detaching every network as it goes. This is the only thing that ends one.","operationId":"delete_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Tunnel deleted"},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"patch":{"tags":["Tunnels"],"description":"Rename a tunnel or change its slug. Only the labels move: the keys, addresses, routes and attached networks are untouched, so a connected client sees nothing change and the config you saved keeps working.","operationId":"update_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTunnelRequest"}}},"required":true},"responses":{"200":{"description":"The updated tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"400":{"description":"BAD_REQUEST: invalid slug or display name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another tunnel in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tunnels/{tunnelIdOrSlug}/rotate-key":{"post":{"tags":["Tunnels"],"description":"Replace a tunnel's keys, keeping its id and every attached network's address. Use this if you lost the config or need to revoke one that leaked: the old keys stop working, and the response carries the only copy of the new private key you will get.","operationId":"rotate_tunnel_key","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateTunnelKeyRequest"}}},"required":true},"responses":{"200":{"description":"Keys rotated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTunnel"}}}},"400":{"description":"BAD_REQUEST: that is not a valid WireGuard public key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such tunnel in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/tunnels/{tunnelIdOrSlug}/vpcs/{vpcIdOrSlug}":{"post":{"tags":["Tunnels"],"description":"Attach a private network to a tunnel. The tunnel gains an address inside the network — what that network's VMs see as your address — and a connected client can reach the network immediately, with no config change. Omit `ipv4` and an address is assigned; supply one to pin it. The network's CIDRs must fall inside the tunnel's routes and must not overlap another attached network's.","operationId":"attach_vpc_to_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachVpcRequest"}}},"required":true},"responses":{"200":{"description":"Network attached; the updated tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"400":{"description":"BAD_REQUEST: invalid address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such tunnel or network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: already attached, that address is already taken, the network's CIDRs overlap an already-attached network's, or they fall outside the tunnel's routes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Tunnels"],"description":"Detach a private network from a tunnel: it stops being reachable and the tunnel's address inside it is released. The tunnel — and any connected client — carries on untouched.","operationId":"detach_vpc_from_tunnel","parameters":[{"name":"tunnelIdOrSlug","in":"path","description":"Tunnel id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Network detached; the updated tunnel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tunnel"}}}},"404":{"description":"NOT_FOUND: no such tunnel, or the network is not attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/verifications":{"get":{"tags":["Domains"],"description":"List your verification challenges.","operationId":"list_verifications","parameters":[{"name":"state","in":"query","description":"Narrow to one state. Omit for every challenge the account holds,\nverified records included.","required":false,"schema":{"$ref":"#/components/schemas/VerificationState"}}],"responses":{"200":{"description":"The account's verification challenges","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVerificationsResponse"}}}}}},"post":{"tags":["Domains"],"description":"Issue a challenge. Publish the returned `verificationCode` as a TXT record at `recordName`, then PUT the verification to complete it.","operationId":"create_verification","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVerificationRequest"}}},"required":true},"responses":{"200":{"description":"Verification challenge issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerification"}}}},"400":{"description":"INVALID_DOMAIN: not a valid domain name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/verifications/{domainOrId}":{"get":{"tags":["Domains"],"description":"Fetch a verification challenge by its id or its domain.","operationId":"get_verification","parameters":[{"name":"domainOrId","in":"path","description":"Verification id or the domain under verification","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The pending verification challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerification"}}}},"404":{"description":"NOT_FOUND: no verification challenge for the id or domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"put":{"tags":["Domains"],"description":"Check DNS and record ownership. Addressed by id, only that challenge's own code counts, and the result names it. Use this when several people are verifying the same domain.","operationId":"verify_domain","parameters":[{"name":"domainOrId","in":"path","description":"Verification id, or the domain to verify","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"TXT record checked; ownership recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerified"}}}},"400":{"description":"VERIFICATION_FAILED: no TXT record at the challenge's recordName matches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no verification challenge for the id or domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Domains"],"description":"Withdraw outstanding challenges; by domain this drops all of them. A challenge that already verified is a permanent record and cannot be deleted.","operationId":"delete_verification","parameters":[{"name":"domainOrId","in":"path","description":"Verification id or the domain under verification","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Verification challenge discarded"},"404":{"description":"NOT_FOUND: no verification challenge for the id or domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"VERIFICATION_IMMUTABLE: that challenge already verified and is a permanent record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms":{"get":{"tags":["VMs"],"description":"List your VMs.","operationId":"list_vms","parameters":[{"name":"state","in":"query","description":"Only VMs in this state.","required":false,"schema":{"$ref":"#/components/schemas/VmState"}},{"name":"slug","in":"query","description":"Exact slug match.","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"snapshotId","in":"query","description":"Only VMs booted from this snapshot.","required":false,"schema":{"type":"string"}},{"name":"vpc","in":"query","description":"Only VMs attached to this network (id, or your slug for it).","required":false,"schema":{"type":"string"}},{"name":"metadata","in":"query","description":"Comma-separated `key:value` pairs, e.g. `env:prod,region:us-west`.","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's VMs, with per-state counts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVmsResponse"}}}},"404":{"description":"NOT_FOUND: the `vpc` filter names a network that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"post":{"tags":["VMs"],"description":"Boot a new VM.","operationId":"create_vm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVmRequest"}}},"required":true},"responses":{"201":{"description":"VM created and booting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: invalid slug, metadata, or a snapshot that does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another VM in your account (pass `reassignSlug` to take it), or no capacity is available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"429":{"description":"LIMIT_EXCEEDED: your plan's limit for this resource is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}":{"get":{"tags":["VMs"],"description":"Fetch a VM by its id or its slug.","operationId":"get_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["VMs"],"description":"Permanently destroy the VM.","operationId":"delete_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"VM deleted"},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"patch":{"tags":["VMs"],"description":"Rename a VM, change its slug or idle timeout, or merge in metadata.","operationId":"update_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVmRequest"}}},"required":true},"responses":{"200":{"description":"The updated VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: invalid slug or metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another VM in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/exec-await":{"post":{"tags":["VMs"],"description":"Run a command in the guest and wait for it to finish. A non-zero exit status is still a 200. Read `statusCode`, which is null if the command was killed by its timeout.","operationId":"exec_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecRequest"}}},"required":true},"responses":{"200":{"description":"The command finished (a non-zero exit is still a 200)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecResponse"}}}},"400":{"description":"BAD_REQUEST: timeoutMs outside 1–300000, or stdin over 1 MiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"403":{"description":"FORBIDDEN: the identity access token is not allowed to use this VM or guest Linux user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is not running, or it stopped responding mid-command (`VM_NON_RESPONSIVE` — the command may already have run; do not retry blindly)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/v5/vms/{vmIdOrSlug}/fs/dir":{"get":{"tags":["Filesystem"],"description":"List the entries of a directory inside the VM.","operationId":"read_dir","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Directory listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadDirResponse"}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/exists":{"get":{"tags":["Filesystem"],"description":"Check whether a path exists inside the VM.","operationId":"path_exists","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Whether the path exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathExistsResponse"}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/mkdir":{"post":{"tags":["Filesystem"],"description":"Create a directory inside the VM.","operationId":"make_dir","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeDirRequest"}}},"required":true},"responses":{"204":{"description":"Directory created, parents included"},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/read":{"get":{"tags":["Filesystem"],"description":"Read a file from inside the VM. The response body is the file's raw bytes, streamed from the guest, so there is no size limit and nothing to decode. Send a `Range: bytes=…` header to fetch part of a file — which is also how you resume a download that failed partway.","operationId":"read_file","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}},{"name":"Range","in":"header","description":"Optional single byte range, e.g. `bytes=0-1048575`","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"The file's bytes","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}}},"206":{"description":"The requested byte range","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM stopped responding while serving the read (`VM_NON_RESPONSIVE`); safe to retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"416":{"description":"RANGE_NOT_SATISFIABLE: the range lies outside the file"}}}},"/v5/vms/{vmIdOrSlug}/fs/remove":{"delete":{"tags":["Filesystem"],"description":"Delete a file or directory inside the VM.","operationId":"remove_path","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Path removed"},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/stat":{"get":{"tags":["Filesystem"],"description":"Fetch metadata for a path inside the VM.","operationId":"stat_path","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Path metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileStat"}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/uploads":{"post":{"tags":["Filesystem"],"description":"Start a chunked upload of a large file (up to 16 GiB) into the VM. Send the bytes with `PUT /fs/uploads/{uploadId}`, then finish with `commit`.","operationId":"begin_upload","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeginUploadRequest"}}},"required":true},"responses":{"200":{"description":"Session created; upload chunks next","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatus"}}}},"400":{"description":"BAD_REQUEST: invalid path, size over 16 GiB, or malformed sha256","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: too many open upload sessions for this VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/uploads/{uploadId}":{"get":{"tags":["Filesystem"],"description":"Fetch an upload session's progress — use `receivedBytes` as the next chunk's offset when resuming after a failed chunk.","operationId":"upload_status","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatus"}}}},"404":{"description":"NOT_FOUND: no such session (sessions expire after an hour idle and do not survive host restarts)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"put":{"tags":["Filesystem"],"description":"Append one chunk of raw bytes to an upload session at the given offset. Chunks must arrive in order; on any transport error, re-fetch the session and resume from its `receivedBytes`. Keep chunks small enough to retry cheaply (the SDK uses 16 MiB; the hard cap is 256 MiB).","operationId":"upload_chunk","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}},{"name":"offset","in":"query","description":"Byte offset this chunk starts at. Must equal the session's current\n`receivedBytes`; chunks at an already-received offset are acknowledged\nwithout rewriting (safe retries), and offsets beyond it are rejected.","required":true,"schema":{"type":"integer","format":"int64","minimum":0}}],"requestBody":{"description":"The chunk's raw bytes","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}},"required":true},"responses":{"200":{"description":"Chunk stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatus"}}}},"400":{"description":"BAD_REQUEST: the chunk overruns the declared size","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the offset leaves a gap, or the session is committing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"413":{"description":"PAYLOAD_TOO_LARGE: the chunk exceeds 256 MiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["Filesystem"],"description":"Abandon an upload session and free its server-side spool space.","operationId":"abort_upload","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Session discarded"},"404":{"description":"NOT_FOUND: no such session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the session is committing and cannot be aborted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/uploads/{uploadId}/commit":{"post":{"tags":["Filesystem"],"description":"Finish an upload: the file is verified (size, and sha256 when declared) and renamed into place inside the guest atomically — the target path never holds a partial file. On success the session is gone; on failure it remains and commit may be retried.","operationId":"commit_upload","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"File committed"},"400":{"description":"BAD_REQUEST: not all bytes were uploaded, or the sha256 does not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: a commit is already in progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/fs/write":{"put":{"tags":["Filesystem"],"description":"Write a file inside the VM, atomically: the path holds the old file until the new one has arrived intact, never a half-written one.\n\nSend the file as the raw request body (with `?path=`) and it can be as large as 16 GiB — nothing buffers it whole. Send a JSON body instead (up to 32 MiB) when it is easier to type than to upload, as from this page.\n\nThis is a single attempt: if the connection drops, the write starts over. To upload something big over a link you do not trust, open an upload session (`POST /fs/uploads`) and send retryable chunks instead.","operationId":"write_file","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest. Required when the body is raw bytes;\nomitted when sending a JSON body, which carries its own `path`.","required":false,"schema":{"type":"string"}},{"name":"mode","in":"query","description":"Final file mode bits (e.g. 493 for `0o755`). Defaults to the target's\nexisting mode, or `0o600` for a new file.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"sha256","in":"query","description":"Optional sha256 of the file as 64 hex characters. Supplied, the write is\nrejected unless the received bytes match.","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteFileRequest"}},"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}},"required":true},"responses":{"204":{"description":"File written"},"400":{"description":"BAD_REQUEST: invalid path, malformed body, or a sha256 that does not match the bytes received","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM stopped responding around the commit (`VM_NON_RESPONSIVE` — the upload may have committed; read the file back before re-uploading)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"413":{"description":"PAYLOAD_TOO_LARGE: a JSON body over 32 MiB, or a file over 16 GiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/networks":{"put":{"tags":["VMs"],"description":"Set the VM's private networks — attach, update, or detach in one declarative call (an empty list detaches). Works while the VM is stopped, running, or paused: a stopped VM picks the change up on its next start, a running VM is reconfigured live, and a paused VM applies it on resume. At most one network is accepted today. The VM keeps its address while it stays in the same network; moving networks (or naming a different address) renumbers it.","operationId":"update_vm_networks","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVmNetworksRequest"}}},"required":true},"responses":{"200":{"description":"The updated VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: more than one network, an address outside the network CIDR, or invalid routes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or the named network does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is starting or pausing, the address is already reserved, or the guest cannot be reconfigured live (shut it down and start it again instead)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/pause":{"post":{"tags":["VMs"],"description":"Freeze a running VM, keeping its memory so a later start resumes it exactly.","operationId":"pause_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The paused VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is not running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/pty":{"get":{"tags":["VMs"],"description":"Open an interactive terminal on the VM over a WebSocket. The session outlives this connection: closing the socket only detaches, so it can be reattached or listed later. It also outlives the host that serves it — a host restart reattaches this socket to the same session and replays its scrollback behind a terminal reset, with no break in the stream.","operationId":"open_pty","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"exec","in":"query","description":"Command to run; omit for a login shell","required":false,"schema":{"type":"string"}},{"name":"cols","in":"query","description":"Initial width in columns (default 80)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"rows","in":"query","description":"Initial height in rows (default 24)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"linuxUser","in":"query","description":"Guest Linux user to run as; omit for the guest's default user (uid 1000, else root)","required":false,"schema":{"type":"string"}},{"name":"slug","in":"query","description":"Name this session so you can reattach to it later without storing the id we mint. If a session already has this name you get that one back — alive, or exited with its final output and exit code still readable — and `exec` is NOT run; the `sessionInfo` frame's `created` says which happened. Starting over is DELETE then open again. Must not be all digits: session ids are bare integers, so an all-digit name could not be told apart from one.","required":false,"schema":{"type":"string"}},{"name":"replaceOnExit","in":"query","description":"Respawn the shell in place when it exits, keeping this session's id, name and scrollback — for a terminal that should outlive whatever runs in it. A command that dies at startup is not respawned forever; after a few exits in quick succession the session is left dead so the error is readable. Leave this off if you are waiting on a command's exit code: a session that comes back to life reports `running` again.","required":false,"schema":{"type":"boolean"}}],"responses":{"101":{"description":"Switching protocols. The first text frame carries `{\"type\":\"sessionInfo\",\"sessionId\":N,\"slug\":\"…\",\"created\":true}`; after that, binary frames are terminal I/O and text frames are `resize`/`signal` in, `exited`/`error` out."},"400":{"description":"BAD_REQUEST: not a WebSocket upgrade request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM stopped responding during the terminal handshake (`VM_NON_RESPONSIVE`); the session may have been created, so list sessions before opening another","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/v5/vms/{vmIdOrSlug}/pty/sessions":{"get":{"tags":["VMs"],"description":"List the VM's terminal sessions, running and recently exited. Scoped to one Linux user when the request names one, otherwise every session on the VM.","operationId":"list_pty_sessions","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"linuxUser","in":"query","description":"Only sessions owned by this guest Linux user; omit for all of them","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The VM's terminal sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPtySessionsResponse"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/v5/vms/{vmIdOrSlug}/pty/sessions/{sessionId}":{"get":{"tags":["VMs"],"description":"Reattach to an existing terminal session over a WebSocket, addressed by id or by the slug it was opened with. The retained scrollback replays first, then live output follows.","operationId":"attach_pty","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","description":"Session id, or the slug the session was opened with","required":true,"schema":{"type":"string"}},{"name":"linuxUser","in":"query","description":"Guest Linux user that must own the session","required":false,"schema":{"type":"string"}}],"responses":{"101":{"description":"Switching protocols. Same frame protocol as opening a terminal, including the initial `sessionInfo` frame — a caller that attached by slug has not seen the id yet."},"400":{"description":"BAD_REQUEST: not a WebSocket upgrade request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM or session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"security":[{"apiKey":[]},{"identityAccessToken":[]}]},"delete":{"tags":["VMs"],"description":"Kill a terminal session and remove it, addressed by id or by the slug it was opened with.","operationId":"close_pty_session","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","description":"Session id, or the slug the session was opened with","required":true,"schema":{"type":"string"}},{"name":"linuxUser","in":"query","description":"Guest Linux user that must own the session","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session killed and removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClosePtySessionResponse"}}}},"404":{"description":"NOT_FOUND: no such VM or session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/v5/vms/{vmIdOrSlug}/resize":{"post":{"tags":["VMs"],"description":"Change a VM's vCPU, memory, or disk. Resizing is grow-only on every axis: each can go up but never down. Omitted fields keep their current value. vCPU and memory apply live to a running VM, apply on resume for a paused one, and apply at the next boot for a stopped one. Growing the disk needs a running VM, since it grows a live block device and then its filesystem.","operationId":"resize_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResizeVmRequest"}}},"required":true},"responses":{"200":{"description":"The resized VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: requested size is below the current size on some axis","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the disk can only grow while the VM is running, or the node is out of capacity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"429":{"description":"LIMIT_EXCEEDED: your plan's limit for this resource is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/snapshot":{"post":{"tags":["Snapshots"],"description":"Capture the VM's exact state, memory and disk, so a VM booted from it resumes exactly where this one was. The VM must be running or paused. New snapshots are private. This call returns once the state is captured; VMs can be created from the snapshot immediately.","operationId":"snapshot_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSnapshotRequest"}}},"required":true},"responses":{"200":{"description":"Snapshot created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotCreated"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is neither running nor paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vms/{vmIdOrSlug}/start":{"post":{"tags":["VMs"],"description":"Boot a stopped VM, or resume a paused one.","operationId":"start_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The starting VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is ephemeral and was deleted when it shut down, or no capacity is available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vpcs":{"get":{"tags":["VPCs"],"description":"List your private networks, newest first.","operationId":"list_vpcs","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's private networks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVpcsResponse"}}}}}},"post":{"tags":["VPCs"],"description":"Create a private network.","operationId":"create_vpc","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVpcRequest"}}},"required":true},"responses":{"200":{"description":"Network created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vpc"}}}},"400":{"description":"BAD_REQUEST: invalid CIDR or slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another network in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vpcs/{vpcIdOrSlug}":{"get":{"tags":["VPCs"],"description":"Fetch a private network by its id or its slug.","operationId":"get_vpc","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vpc"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"delete":{"tags":["VPCs"],"description":"Delete a private network.","operationId":"delete_vpc","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Network deleted"},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the network still has VMs attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}},"patch":{"tags":["VPCs"],"description":"Rename a private network or change its slug.","operationId":"update_vpc","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVpcRequest"}}},"required":true},"responses":{"200":{"description":"The updated network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vpc"}}}},"400":{"description":"BAD_REQUEST: invalid slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another network in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vpcs/{vpcIdOrSlug}/ips":{"get":{"tags":["VPCs"],"description":"List the addresses currently reserved in a private network: each attached VM's address, plus one per VPN.","operationId":"list_vpc_ips","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The network's reserved addresses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVpcIpsResponse"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/v5/vpcs/{vpcIdOrSlug}/tunnels":{"get":{"tags":["VPCs"],"description":"List the tunnels attached to a private network, newest first. Listings never include a private key: one is returned only by create and rotate-key.","operationId":"list_vpc_tunnels","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The network's tunnels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTunnelsResponse"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}}}},"/verifications":{"get":{"tags":["Domains"],"description":"List your verification challenges.","operationId":"list_verifications","parameters":[{"name":"state","in":"query","description":"Narrow to one state. Omit for every challenge the account holds,\nverified records included.","required":false,"schema":{"$ref":"#/components/schemas/VerificationState"}}],"responses":{"200":{"description":"The account's verification challenges","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVerificationsResponse"}}}}},"deprecated":true},"post":{"tags":["Domains"],"description":"Issue a challenge. Publish the returned `verificationCode` as a TXT record at `recordName`, then PUT the verification to complete it.","operationId":"create_verification","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVerificationRequest"}}},"required":true},"responses":{"200":{"description":"Verification challenge issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerification"}}}},"400":{"description":"INVALID_DOMAIN: not a valid domain name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/verifications/{domainOrId}":{"get":{"tags":["Domains"],"description":"Fetch a verification challenge by its id or its domain.","operationId":"get_verification","parameters":[{"name":"domainOrId","in":"path","description":"Verification id or the domain under verification","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The pending verification challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerification"}}}},"404":{"description":"NOT_FOUND: no verification challenge for the id or domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"put":{"tags":["Domains"],"description":"Check DNS and record ownership. Addressed by id, only that challenge's own code counts, and the result names it. Use this when several people are verifying the same domain.","operationId":"verify_domain","parameters":[{"name":"domainOrId","in":"path","description":"Verification id, or the domain to verify","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"TXT record checked; ownership recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerified"}}}},"400":{"description":"VERIFICATION_FAILED: no TXT record at the challenge's recordName matches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no verification challenge for the id or domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Domains"],"description":"Withdraw outstanding challenges; by domain this drops all of them. A challenge that already verified is a permanent record and cannot be deleted.","operationId":"delete_verification","parameters":[{"name":"domainOrId","in":"path","description":"Verification id or the domain under verification","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Verification challenge discarded"},"404":{"description":"NOT_FOUND: no verification challenge for the id or domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"VERIFICATION_IMMUTABLE: that challenge already verified and is a permanent record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms":{"get":{"tags":["VMs"],"description":"List your VMs.","operationId":"list_vms","parameters":[{"name":"state","in":"query","description":"Only VMs in this state.","required":false,"schema":{"$ref":"#/components/schemas/VmState"}},{"name":"slug","in":"query","description":"Exact slug match.","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"snapshotId","in":"query","description":"Only VMs booted from this snapshot.","required":false,"schema":{"type":"string"}},{"name":"vpc","in":"query","description":"Only VMs attached to this network (id, or your slug for it).","required":false,"schema":{"type":"string"}},{"name":"metadata","in":"query","description":"Comma-separated `key:value` pairs, e.g. `env:prod,region:us-west`.","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's VMs, with per-state counts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVmsResponse"}}}},"404":{"description":"NOT_FOUND: the `vpc` filter names a network that does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"post":{"tags":["VMs"],"description":"Boot a new VM.","operationId":"create_vm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVmRequest"}}},"required":true},"responses":{"201":{"description":"VM created and booting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: invalid slug, metadata, or a snapshot that does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another VM in your account (pass `reassignSlug` to take it), or no capacity is available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"429":{"description":"LIMIT_EXCEEDED: your plan's limit for this resource is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}":{"get":{"tags":["VMs"],"description":"Fetch a VM by its id or its slug.","operationId":"get_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["VMs"],"description":"Permanently destroy the VM.","operationId":"delete_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"VM deleted"},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"patch":{"tags":["VMs"],"description":"Rename a VM, change its slug or idle timeout, or merge in metadata.","operationId":"update_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVmRequest"}}},"required":true},"responses":{"200":{"description":"The updated VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: invalid slug or metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another VM in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/exec-await":{"post":{"tags":["VMs"],"description":"Run a command in the guest and wait for it to finish. A non-zero exit status is still a 200. Read `statusCode`, which is null if the command was killed by its timeout.","operationId":"exec_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecRequest"}}},"required":true},"responses":{"200":{"description":"The command finished (a non-zero exit is still a 200)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecResponse"}}}},"400":{"description":"BAD_REQUEST: timeoutMs outside 1–300000, or stdin over 1 MiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"403":{"description":"FORBIDDEN: the identity access token is not allowed to use this VM or guest Linux user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is not running, or it stopped responding mid-command (`VM_NON_RESPONSIVE` — the command may already have run; do not retry blindly)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true,"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/vms/{vmIdOrSlug}/fs/dir":{"get":{"tags":["Filesystem"],"description":"List the entries of a directory inside the VM.","operationId":"read_dir","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Directory listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadDirResponse"}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/exists":{"get":{"tags":["Filesystem"],"description":"Check whether a path exists inside the VM.","operationId":"path_exists","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Whether the path exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathExistsResponse"}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/mkdir":{"post":{"tags":["Filesystem"],"description":"Create a directory inside the VM.","operationId":"make_dir","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MakeDirRequest"}}},"required":true},"responses":{"204":{"description":"Directory created, parents included"},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/read":{"get":{"tags":["Filesystem"],"description":"Read a file from inside the VM. The response body is the file's raw bytes, streamed from the guest, so there is no size limit and nothing to decode. Send a `Range: bytes=…` header to fetch part of a file — which is also how you resume a download that failed partway.","operationId":"read_file","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}},{"name":"Range","in":"header","description":"Optional single byte range, e.g. `bytes=0-1048575`","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"The file's bytes","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}}},"206":{"description":"The requested byte range","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM stopped responding while serving the read (`VM_NON_RESPONSIVE`); safe to retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"416":{"description":"RANGE_NOT_SATISFIABLE: the range lies outside the file"}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/remove":{"delete":{"tags":["Filesystem"],"description":"Delete a file or directory inside the VM.","operationId":"remove_path","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Path removed"},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/stat":{"get":{"tags":["Filesystem"],"description":"Fetch metadata for a path inside the VM.","operationId":"stat_path","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Path metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileStat"}}}},"400":{"description":"BAD_REQUEST: the path is not absolute, contains `..`, or is over 4096 characters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or no such path in the guest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/uploads":{"post":{"tags":["Filesystem"],"description":"Start a chunked upload of a large file (up to 16 GiB) into the VM. Send the bytes with `PUT /fs/uploads/{uploadId}`, then finish with `commit`.","operationId":"begin_upload","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BeginUploadRequest"}}},"required":true},"responses":{"200":{"description":"Session created; upload chunks next","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatus"}}}},"400":{"description":"BAD_REQUEST: invalid path, size over 16 GiB, or malformed sha256","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: too many open upload sessions for this VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/uploads/{uploadId}":{"get":{"tags":["Filesystem"],"description":"Fetch an upload session's progress — use `receivedBytes` as the next chunk's offset when resuming after a failed chunk.","operationId":"upload_status","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatus"}}}},"404":{"description":"NOT_FOUND: no such session (sessions expire after an hour idle and do not survive host restarts)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"put":{"tags":["Filesystem"],"description":"Append one chunk of raw bytes to an upload session at the given offset. Chunks must arrive in order; on any transport error, re-fetch the session and resume from its `receivedBytes`. Keep chunks small enough to retry cheaply (the SDK uses 16 MiB; the hard cap is 256 MiB).","operationId":"upload_chunk","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}},{"name":"offset","in":"query","description":"Byte offset this chunk starts at. Must equal the session's current\n`receivedBytes`; chunks at an already-received offset are acknowledged\nwithout rewriting (safe retries), and offsets beyond it are rejected.","required":true,"schema":{"type":"integer","format":"int64","minimum":0}}],"requestBody":{"description":"The chunk's raw bytes","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}},"required":true},"responses":{"200":{"description":"Chunk stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadSessionStatus"}}}},"400":{"description":"BAD_REQUEST: the chunk overruns the declared size","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the offset leaves a gap, or the session is committing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"413":{"description":"PAYLOAD_TOO_LARGE: the chunk exceeds 256 MiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["Filesystem"],"description":"Abandon an upload session and free its server-side spool space.","operationId":"abort_upload","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Session discarded"},"404":{"description":"NOT_FOUND: no such session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the session is committing and cannot be aborted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/uploads/{uploadId}/commit":{"post":{"tags":["Filesystem"],"description":"Finish an upload: the file is verified (size, and sha256 when declared) and renamed into place inside the guest atomically — the target path never holds a partial file. On success the session is gone; on failure it remains and commit may be retried.","operationId":"commit_upload","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"uploadId","in":"path","description":"Upload session id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"File committed"},"400":{"description":"BAD_REQUEST: not all bytes were uploaded, or the sha256 does not match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: a commit is already in progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/fs/write":{"put":{"tags":["Filesystem"],"description":"Write a file inside the VM, atomically: the path holds the old file until the new one has arrived intact, never a half-written one.\n\nSend the file as the raw request body (with `?path=`) and it can be as large as 16 GiB — nothing buffers it whole. Send a JSON body instead (up to 32 MiB) when it is easier to type than to upload, as from this page.\n\nThis is a single attempt: if the connection drops, the write starts over. To upload something big over a link you do not trust, open an upload session (`POST /fs/uploads`) and send retryable chunks instead.","operationId":"write_file","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"path","in":"query","description":"Absolute path inside the guest. Required when the body is raw bytes;\nomitted when sending a JSON body, which carries its own `path`.","required":false,"schema":{"type":"string"}},{"name":"mode","in":"query","description":"Final file mode bits (e.g. 493 for `0o755`). Defaults to the target's\nexisting mode, or `0o600` for a new file.","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"sha256","in":"query","description":"Optional sha256 of the file as 64 hex characters. Supplied, the write is\nrejected unless the received bytes match.","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteFileRequest"}},"application/octet-stream":{"schema":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}},"required":true},"responses":{"204":{"description":"File written"},"400":{"description":"BAD_REQUEST: invalid path, malformed body, or a sha256 that does not match the bytes received","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM stopped responding around the commit (`VM_NON_RESPONSIVE` — the upload may have committed; read the file back before re-uploading)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"413":{"description":"PAYLOAD_TOO_LARGE: a JSON body over 32 MiB, or a file over 16 GiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/networks":{"put":{"tags":["VMs"],"description":"Set the VM's private networks — attach, update, or detach in one declarative call (an empty list detaches). Works while the VM is stopped, running, or paused: a stopped VM picks the change up on its next start, a running VM is reconfigured live, and a paused VM applies it on resume. At most one network is accepted today. The VM keeps its address while it stays in the same network; moving networks (or naming a different address) renumbers it.","operationId":"update_vm_networks","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVmNetworksRequest"}}},"required":true},"responses":{"200":{"description":"The updated VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: more than one network, an address outside the network CIDR, or invalid routes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM, or the named network does not exist in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is starting or pausing, the address is already reserved, or the guest cannot be reconfigured live (shut it down and start it again instead)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/pause":{"post":{"tags":["VMs"],"description":"Freeze a running VM, keeping its memory so a later start resumes it exactly.","operationId":"pause_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The paused VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is not running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/pty":{"get":{"tags":["VMs"],"description":"Open an interactive terminal on the VM over a WebSocket. The session outlives this connection: closing the socket only detaches, so it can be reattached or listed later. It also outlives the host that serves it — a host restart reattaches this socket to the same session and replays its scrollback behind a terminal reset, with no break in the stream.","operationId":"open_pty","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"exec","in":"query","description":"Command to run; omit for a login shell","required":false,"schema":{"type":"string"}},{"name":"cols","in":"query","description":"Initial width in columns (default 80)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"rows","in":"query","description":"Initial height in rows (default 24)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"linuxUser","in":"query","description":"Guest Linux user to run as; omit for the guest's default user (uid 1000, else root)","required":false,"schema":{"type":"string"}},{"name":"slug","in":"query","description":"Name this session so you can reattach to it later without storing the id we mint. If a session already has this name you get that one back — alive, or exited with its final output and exit code still readable — and `exec` is NOT run; the `sessionInfo` frame's `created` says which happened. Starting over is DELETE then open again. Must not be all digits: session ids are bare integers, so an all-digit name could not be told apart from one.","required":false,"schema":{"type":"string"}},{"name":"replaceOnExit","in":"query","description":"Respawn the shell in place when it exits, keeping this session's id, name and scrollback — for a terminal that should outlive whatever runs in it. A command that dies at startup is not respawned forever; after a few exits in quick succession the session is left dead so the error is readable. Leave this off if you are waiting on a command's exit code: a session that comes back to life reports `running` again.","required":false,"schema":{"type":"boolean"}}],"responses":{"101":{"description":"Switching protocols. The first text frame carries `{\"type\":\"sessionInfo\",\"sessionId\":N,\"slug\":\"…\",\"created\":true}`; after that, binary frames are terminal I/O and text frames are `resize`/`signal` in, `exited`/`error` out."},"400":{"description":"BAD_REQUEST: not a WebSocket upgrade request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM stopped responding during the terminal handshake (`VM_NON_RESPONSIVE`); the session may have been created, so list sessions before opening another","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true,"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/vms/{vmIdOrSlug}/pty/sessions":{"get":{"tags":["VMs"],"description":"List the VM's terminal sessions, running and recently exited. Scoped to one Linux user when the request names one, otherwise every session on the VM.","operationId":"list_pty_sessions","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"linuxUser","in":"query","description":"Only sessions owned by this guest Linux user; omit for all of them","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The VM's terminal sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPtySessionsResponse"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true,"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/vms/{vmIdOrSlug}/pty/sessions/{sessionId}":{"get":{"tags":["VMs"],"description":"Reattach to an existing terminal session over a WebSocket, addressed by id or by the slug it was opened with. The retained scrollback replays first, then live output follows.","operationId":"attach_pty","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","description":"Session id, or the slug the session was opened with","required":true,"schema":{"type":"string"}},{"name":"linuxUser","in":"query","description":"Guest Linux user that must own the session","required":false,"schema":{"type":"string"}}],"responses":{"101":{"description":"Switching protocols. Same frame protocol as opening a terminal, including the initial `sessionInfo` frame — a caller that attached by slug has not seen the id yet."},"400":{"description":"BAD_REQUEST: not a WebSocket upgrade request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM or session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true,"security":[{"apiKey":[]},{"identityAccessToken":[]}]},"delete":{"tags":["VMs"],"description":"Kill a terminal session and remove it, addressed by id or by the slug it was opened with.","operationId":"close_pty_session","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}},{"name":"sessionId","in":"path","description":"Session id, or the slug the session was opened with","required":true,"schema":{"type":"string"}},{"name":"linuxUser","in":"query","description":"Guest Linux user that must own the session","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session killed and removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClosePtySessionResponse"}}}},"404":{"description":"NOT_FOUND: no such VM or session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true,"security":[{"apiKey":[]},{"identityAccessToken":[]}]}},"/vms/{vmIdOrSlug}/resize":{"post":{"tags":["VMs"],"description":"Change a VM's vCPU, memory, or disk. Resizing is grow-only on every axis: each can go up but never down. Omitted fields keep their current value. vCPU and memory apply live to a running VM, apply on resume for a paused one, and apply at the next boot for a stopped one. Growing the disk needs a running VM, since it grows a live block device and then its filesystem.","operationId":"resize_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResizeVmRequest"}}},"required":true},"responses":{"200":{"description":"The resized VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"400":{"description":"BAD_REQUEST: requested size is below the current size on some axis","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the disk can only grow while the VM is running, or the node is out of capacity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"429":{"description":"LIMIT_EXCEEDED: your plan's limit for this resource is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/snapshot":{"post":{"tags":["Snapshots"],"description":"Capture the VM's exact state, memory and disk, so a VM booted from it resumes exactly where this one was. The VM must be running or paused. New snapshots are private. This call returns once the state is captured; VMs can be created from the snapshot immediately.","operationId":"snapshot_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSnapshotRequest"}}},"required":true},"responses":{"200":{"description":"Snapshot created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotCreated"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is neither running nor paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vms/{vmIdOrSlug}/start":{"post":{"tags":["VMs"],"description":"Boot a stopped VM, or resume a paused one.","operationId":"start_vm","parameters":[{"name":"vmIdOrSlug","in":"path","description":"VM id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The starting VM","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vm"}}}},"404":{"description":"NOT_FOUND: no such VM in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the VM is ephemeral and was deleted when it shut down, or no capacity is available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vpcs":{"get":{"tags":["VPCs"],"description":"List your private networks, newest first.","operationId":"list_vpcs","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The account's private networks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVpcsResponse"}}}}},"deprecated":true},"post":{"tags":["VPCs"],"description":"Create a private network.","operationId":"create_vpc","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVpcRequest"}}},"required":true},"responses":{"200":{"description":"Network created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vpc"}}}},"400":{"description":"BAD_REQUEST: invalid CIDR or slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another network in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vpcs/{vpcIdOrSlug}":{"get":{"tags":["VPCs"],"description":"Fetch a private network by its id or its slug.","operationId":"get_vpc","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vpc"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"delete":{"tags":["VPCs"],"description":"Delete a private network.","operationId":"delete_vpc","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Network deleted"},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: the network still has VMs attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true},"patch":{"tags":["VPCs"],"description":"Rename a private network or change its slug.","operationId":"update_vpc","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVpcRequest"}}},"required":true},"responses":{"200":{"description":"The updated network","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Vpc"}}}},"400":{"description":"BAD_REQUEST: invalid slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}},"409":{"description":"CONFLICT: that slug is already used by another network in your account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vpcs/{vpcIdOrSlug}/ips":{"get":{"tags":["VPCs"],"description":"List the addresses currently reserved in a private network: each attached VM's address, plus one per VPN.","operationId":"list_vpc_ips","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The network's reserved addresses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVpcIpsResponse"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}},"/vpcs/{vpcIdOrSlug}/tunnels":{"get":{"tags":["VPCs"],"description":"List the tunnels attached to a private network, newest first. Listings never include a private key: one is returned only by create and rotate-key.","operationId":"list_vpc_tunnels","parameters":[{"name":"vpcIdOrSlug","in":"path","description":"VPC id, or your slug for it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The network's tunnels","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTunnelsResponse"}}}},"404":{"description":"NOT_FOUND: no such network in this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicErrorBody"}}}}},"deprecated":true}}},"components":{"schemas":{"AccessTokenInfo":{"type":"object","required":["id"],"properties":{"id":{"$ref":"#/components/schemas/IdentityTokenId"}}},"AccountId":{"type":"string","description":"Identifies an account. New accounts are minted by the identity\nservice's registry as `\"acct-<uuid>\"`; accounts created before the\nprefix era are bare UUID strings, and the built-in `freestyle`\ninfrastructure account is the literal `\"freestyle\"`. All three forms\nare equally valid ids: the prefix is a minting convention, not a\nformat requirement."},"AddOperation":{"type":"object","description":"JSON Patch 'add' operation representation","required":["path","value"],"properties":{"path":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nwithin the target document where the operation is performed."},"value":{"description":"Value to add to the target location."}}},"AttachNetwork":{"type":"object","description":"Attach a VM to one of your private networks. A VM may be on at most one.","required":["vpc"],"properties":{"ipv4":{"type":"object","description":"The IPv4 address to take inside the network: an explicit address,\n`true` to auto-allocate, or `false` to opt out. Omitted = allocate one\n(networks are dual-stack), except on a network with no IPv4 CIDR,\nwhere the VM simply joins over IPv6."},"ipv6":{"type":"object","description":"The IPv6 address to take: an explicit address, `true` to auto-allocate\n(the default), or `false` to opt out. Every network has an IPv6 CIDR."},"routes":{"type":"array","items":{"$ref":"#/components/schemas/NetworkRoute"},"description":"Static routes to install in the guest."},"vpc":{"type":"string","description":"The network to join, by id or by your slug for it. Also accepted as\n`vpcId`, the name the rest of the API uses."}}},"AttachVpcRequest":{"type":"object","properties":{"exit":{"type":"boolean","description":"Make your client this network's exit — a router members can name as a\nnext hop. A VM routes through it with an ordinary NIC route via the\nattachment's address, and everything it sends the exit — whatever the\ndestination — is forwarded to your client, which is expected to NAT it\nonward. Several attachments per network may be exits: members choose\nper route, so two exits and two same-metric routes are a redundant\npair, and a dead exit stops being ARP-answered so the other takes\nover by itself."},"ipv4":{"type":["string","null"],"description":"The address you want inside the private network. Omit to be assigned\none. It must be free: a VM already holding it makes this a conflict,\nand once yours, no VM can be given it."},"ipv6":{"type":["string","null"],"description":"An explicit IPv6 address, equivalent to passing one in `ipv4`; only one\nmay be set."},"remoteCidrs":{"type":"array","items":{"type":"string"},"description":"Ranges behind your client that this network routes through the tunnel —\na site-to-site setup where your client machine forwards for other hosts\non its side. Hosts inside these ranges keep their real addresses inside\nthe network. A range carved out of the network's own CIDR just works:\nVMs reach it with no configuration, and the range is excluded from VM\naddress allocation (it must be free when granted). A range outside the\nnetwork's CIDRs is reached by giving VMs a route via the attachment's\n`address`. A range may not straddle the network's CIDR boundary, nor\noverlap a range another tunnel already grants this network."}}},"BackgroundRequestId":{"type":"string","description":"Identifies a backgrounded public-API request (the handle a client\npolls `/background-requests/{id}` with). Minted as `\"bgr-<uuid>\"`."},"BackgroundRequestPending":{"type":"object","description":"Still-running poll answer (`202`).","required":["requestId","status"],"properties":{"requestId":{"$ref":"#/components/schemas/BackgroundRequestId"},"status":{"type":"string","description":"Always `pending`.","example":"pending"}}},"BeginUploadRequest":{"type":"object","description":"Starts an upload session.","required":["path","size"],"properties":{"mode":{"type":["integer","null"],"format":"int32","description":"Optional final file mode bits (e.g. 493 for `0o755`). Defaults to the\ntarget's existing mode, or `0o600` for a new file.","minimum":0},"path":{"type":"string","description":"Absolute path inside the guest the finished file is committed to."},"sha256":{"type":["string","null"],"description":"Optional sha256 of the full file as 64 hex characters; the upload is\nrejected at commit if the received bytes do not match."},"size":{"type":"integer","format":"int64","description":"Total file size in bytes, up to 16 GiB.","minimum":0}}},"CertificateInfo":{"type":"object","required":["domain","wildcard","active","notAfter","generation"],"properties":{"active":{"type":"boolean"},"domain":{"type":"string"},"generation":{"type":"integer","format":"int64"},"notAfter":{"type":"string","format":"date-time"},"wildcard":{"type":"boolean"}}},"ClosePtySessionResponse":{"type":"object","required":["sessionId"],"properties":{"exitCode":{"type":["integer","null"],"format":"int32","description":"Exit code if the session had already exited. Absent when it was still\nrunning: it is sent `SIGHUP` and torn down asynchronously."},"sessionId":{"type":"integer","format":"int64","minimum":0}}},"ContentEncoding":{"type":"string","description":"How `content` is encoded in the JSON form of a write.","enum":["utf8","base64"]},"CopyOperation":{"type":"object","description":"JSON Patch 'copy' operation representation","required":["from","path"],"properties":{"from":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nto copy value from."},"path":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nwithin the target document where the operation is performed."}}},"CreateFirewallRuleRequest":{"type":"object","required":["action","source","destination"],"properties":{"action":{"$ref":"#/components/schemas/FirewallAction","description":"Required, and `allow` is the only accepted value."},"description":{"type":["string","null"],"description":"A free-form note, up to 1024 characters."},"destination":{"$ref":"#/components/schemas/FirewallEndpoint"},"source":{"$ref":"#/components/schemas/FirewallEndpoint"}},"additionalProperties":false},"CreateIdentityBody":{"type":"object","properties":{"managed":{"type":"boolean","description":"Create a managed identity. Managed identities cannot be deleted through\nthe public surface."}}},"CreateSnapshotRequest":{"type":"object","properties":{"autoDeleteSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the snapshot once this many seconds pass without a VM being\ncreated from it. Every create from it resets the clock. -1 (or\nomitting this) means never; some plans cap this, and on those omitting\nit (or sending -1) gets you the cap."},"displayName":{"type":["string","null"]},"slug":{"type":["string","null"]},"ttlSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the snapshot this many seconds after it is taken, whatever has\nbooted it since. -1 (or omitting this) means no deadline."}},"additionalProperties":false},"CreateTlsForwardAuthRequest":{"type":"object","description":"Create or replace a reusable HTTP authorization pre-check.","required":["url"],"properties":{"authResponseHeaders":{"type":"array","items":{"type":"string"},"description":"Headers copied from a successful check onto the VM request."},"headers":{"type":"object","description":"Static headers added to the check. Values are write-only.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"protectedCookies":{"type":"array","items":{"type":"string"},"description":"Cookie names visible to the check but withheld from the VM."},"timeoutMs":{"type":["integer","null"],"format":"int32","description":"Per-check deadline in milliseconds. Defaults to 1500; range 100-5000.","minimum":0},"url":{"type":"string","description":"Absolute HTTPS endpoint receiving a bodyless GET before the VM request."}},"additionalProperties":false},"CreateTlsRuleRequest":{"type":"object","required":["action","domain"],"properties":{"action":{"$ref":"#/components/schemas/TlsAction","description":"Required, and `allow` is the only accepted value."},"description":{"type":["string","null"],"description":"A free-form note, up to 1024 characters."},"destination":{"$ref":"#/components/schemas/TlsEndpoint"},"domain":{"type":"string","description":"The name the session is addressed to: exact, `*.suffix`, or `*`."},"forwardAuth":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TlsForwardAuthReference","description":"Run this reusable authorization check before forwarding to the VM."}]},"match":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/HttpRequestMatch","description":"Vercel-style method/exact-path selector for HTTP egress transforms.\nNon-matching requests pass through without any transform."}]},"protocol":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TlsProtocolDocumented","description":"How the edge treats the bytes. Optional: inferred from `transform` when\none is present, and `tcp` (opaque) otherwise."}]},"source":{"$ref":"#/components/schemas/TlsEndpoint"},"transform":{"type":"array","items":{"$ref":"#/components/schemas/TlsTransformDocumented"},"description":"Rewrites applied in flight, in order. Secrets here are write-only."}},"additionalProperties":false},"CreateTunnelRequest":{"type":"object","properties":{"clientPublicKey":{"type":["string","null"],"description":"The public key of a keypair you already hold. Omit to have one minted\nfor you; supply it and the platform never sees a private key at all."},"displayName":{"type":["string","null"],"description":"A free-form label, for telling several tunnels apart."},"routes":{"type":["array","null"],"items":{"type":"string"},"description":"The ranges your client will route through the tunnel — its\n`AllowedIPs`, fixed for the tunnel's life so attaching and detaching\nnetworks never changes your config. Omit for the default\n(`10.0.0.0/8` and `fd00::/8`), which covers every network with\ndefault addressing. Only networks whose CIDRs fall inside the routes\ncan be attached."},"slug":{"type":["string","null"],"description":"A URL-safe handle, unique within your account: 1–63 chars of\n`[a-z0-9-]`, no leading, trailing, or repeated hyphens. Address the\ntunnel by it instead of by id."},"vpcs":{"type":"array","items":{"$ref":"#/components/schemas/CreateTunnelVpc"},"description":"Networks to attach in the same call, so one request yields a tunnel\nthat already routes somewhere. All-or-nothing: if any attachment is\nrefused, nothing is created."}}},"CreateTunnelVpc":{"type":"object","description":"One network named at create, with the same address choices as an attach.","required":["vpc"],"properties":{"exit":{"type":"boolean","description":"See the attach request's `exit`."},"ipv4":{"type":["string","null"],"description":"The address you want inside it; omit to be assigned one."},"ipv6":{"type":["string","null"],"description":"An explicit IPv6 address instead; only one may be set."},"remoteCidrs":{"type":"array","items":{"type":"string"},"description":"Ranges behind your client this network may route to; see the attach\nrequest's `remoteCidrs`."},"vpc":{"type":"string","description":"The network to attach: a VPC id, or your slug for it. Also accepted as\n`vpcId`."}}},"CreateVerificationRequest":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"}}},"CreateVmRequest":{"type":"object","required":["firewall"],"properties":{"autoDeleteSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the VM once it has gone this many seconds without running.\nEvery start resets the clock; a running VM is never deleted for this.\n-1 (or omitting this) means never; 0 makes the VM ephemeral — deleted\nthe moment it stops.\n\nSome plans cap how long an unused VM is kept. On those, omitting this\n(or sending -1) gets you the cap rather than \"keep forever\", and asking\nfor longer than the cap is a 400."},"automaticRestart":{"type":["boolean","null"],"description":"Boot the VM again if it stops without you asking it to — the VMM\ncrashes, the guest kernel panics, the machine it runs on loses power.\nDefaults to `true`.\n\nIt never fights you: shutting the VM down, pausing it, running\n`poweroff` inside it, or hitting an idle timeout all leave it off. This\nonly decides who gets the last word when a *failure* stops it."},"displayName":{"type":["string","null"]},"firewall":{"$ref":"#/components/schemas/FirewallSpec","description":"Firewall rules to create with the VM, and delete with it. An endpoint\nwith no identity means the VM being created, so `{ \"source\": { \"public\":\ntrue }, \"destination\": { \"port\": 443, \"protocol\": \"tcp\" } }` opens\ninbound HTTPS to it.\n\n**Required.** A VM gets nothing implicitly — no outbound Internet, no\ninbound — so a caller that wants either says so here. `{\"rules\": []}` is\na legitimate answer: a VM reachable only through a mapped domain or SSH."},"idleTimeoutSeconds":{"type":["integer","null"],"format":"int64","description":"Pause the VM after this many seconds without network activity.\n-1 (or omitting this) means never pause for idleness."},"maxRunSeconds":{"type":["integer","null"],"format":"int64","description":"Pause the VM once a single run has lasted this many seconds, however\nbusy it is. Starting it again gives it a fresh budget — unlike\n`maxRunTotalSeconds`, which never resets. -1 (or omitting this) means\nno cap."},"maxRunTotalSeconds":{"type":["integer","null"],"format":"int64","description":"How many seconds this VM may run in total, ever. Spending the budget\npauses the VM, and every later start is a 409 until you raise this.\n-1 (or omitting this) means no budget."},"metadata":{"type":"object","description":"Up to 64 metadata entries; keys and values are limited to 63 characters.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"networks":{"type":"array","items":{"$ref":"#/components/schemas/AttachNetwork"},"description":"Put the VM on your private networks. Omit to leave it off them. At most\none network is accepted today. Also accepted as `vpcs`, the name the\nrest of the API uses."},"reassignSlug":{"type":"boolean","description":"Take `slug` even if another VM in this account already holds it. The\nslug moves atomically: the current holder keeps running, stays\naddressable by id, and just loses its slug. Without this flag a taken\nslug is a 409 CONFLICT. Requires `slug`."},"slug":{"type":["string","null"],"description":"URL-safe identifier, unique within your account: 1–63 chars of\n`[a-z0-9-]`, no leading, trailing, or repeated hyphens."},"snapshotId":{"type":["string","null"],"description":"Boot from this snapshot: its id, your slug for it, or a public\n`{owner}/{slug}`. Omit for the platform default."},"tls":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TlsSpec","description":"TLS rules to create with the VM, and delete with it — named sessions the\nVM may serve or open. An endpoint with no identity means the VM being\ncreated, so `{ \"action\": \"allow\", \"domain\": \"app.acme.com\", \"source\":\n{ \"public\": true }, \"destination\": { \"port\": 8000 } }` publishes that\ndomain to it.\n\nOptional, unlike `firewall`: a TLS rule is a grant layered on top of the\nfirewall's packet decision, never a baseline a VM must state. The created\nrules come back on the response as `tlsRules`, with the ids a later\ndelete needs."}]},"ttlSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the VM this many seconds after it is created, whatever it is\ndoing at the time. A deadline, not an idle window: nothing resets it.\n-1 (or omitting this) means no deadline."}}},"CreateVpcRequest":{"type":"object","properties":{"cidr":{"type":["string","null"],"description":"The network's IPv4 CIDR. Networks are dual-stack; a `/24` out of\n`10.0.0.0/8` is derived for you when omitted. Name one to choose the\nrange yourself — worth doing when the network has to reach an existing\nestate over a tunnel and must not overlap it, or when it needs more\nthan 254 IPv4 members."},"cidrV6":{"type":["string","null"],"description":"The network's IPv6 CIDR; a unique-local /64 is derived for you when\nomitted."},"displayName":{"type":["string","null"]},"firewall":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FirewallSpec","description":"Firewall rules created with the network and deleted with it. A bare\nendpoint means the network being created, so\n`{\"source\":{},\"destination\":{}}` is \"members reach each other\"."}]},"slug":{"type":["string","null"]}}},"CreatedToken":{"type":"object","required":["id","token"],"properties":{"id":{"$ref":"#/components/schemas/IdentityTokenId"},"token":{"type":"string"}}},"CreatedTunnel":{"allOf":[{"$ref":"#/components/schemas/Tunnel"},{"type":"object","required":["clientPrivateKey"],"properties":{"clientPrivateKey":{"type":"string","description":"Returned once. Already embedded in `clientConfig`."}}}],"description":"A tunnel plus a client private key. Returned by the two calls that mint a\nkeypair — create and rotate-key — and nowhere else: the key is never stored\nand cannot be read back. Empty when you supplied your own public key."},"DirEntry":{"type":"object","required":["name","kind"],"properties":{"kind":{"type":"string","description":"`file`, `directory`, or `symlink`."},"name":{"type":"string"}}},"DomainOwnership":{"type":"object","required":["domain","createdAt"],"properties":{"createdAt":{"type":"string","format":"date-time"},"domain":{"type":"string"}}},"DomainVerification":{"type":"object","required":["id","domain","verificationCode","recordName","state","createdAt"],"properties":{"createdAt":{"type":"string","format":"date-time"},"domain":{"type":"string"},"id":{"$ref":"#/components/schemas/DomainVerificationId"},"recordName":{"type":"string","description":"Place `verificationCode` in a TXT record at this name, then verify."},"state":{"$ref":"#/components/schemas/VerificationState"},"verificationCode":{"type":"string"},"verifiedAt":{"type":["string","null"],"format":"date-time","description":"When this challenge proved control; null while pending."}}},"DomainVerificationId":{"type":"string","description":"Identifies a domain-verification challenge. Minted as `\"dv-<uuid>\"`."},"DomainVerified":{"type":"object","description":"A successful verification: ownership, plus which challenge proved it.\nSeveral users may hold challenges for one domain, so `verifiedBy` names the\none whose code was published.","required":["domain","createdAt","verifiedBy"],"properties":{"createdAt":{"type":"string","format":"date-time"},"domain":{"type":"string"},"verifiedBy":{"$ref":"#/components/schemas/DomainVerificationId"}}},"ExecRequest":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"The command line, run through the guest's shell."},"env":{"type":"object","description":"Extra environment variables. Keys must be valid POSIX names.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"linuxUser":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LinuxUserId","description":"Existing in-guest Linux user to run the command as. Omit for the guest's default user:\nwhoever holds uid 1000, or root in an image that has no such account."}]},"stdin":{"type":["string","null"],"description":"Base64-encoded stdin, up to 1 MiB decoded."},"timeoutMs":{"type":["integer","null"],"format":"int64","description":"Wall-clock limit, milliseconds. 1–300000; defaults to 30000 (30 s).\nThe guest kills the command at the deadline.","minimum":0}},"additionalProperties":false},"ExecResponse":{"type":"object","properties":{"statusCode":{"type":["integer","null"],"format":"int32","description":"The command's exit status; absent if it was killed by the timeout."},"stderr":{"type":["string","null"]},"stdout":{"type":["string","null"]}}},"FileStat":{"type":"object","required":["size","isFile","isDirectory","isSymlink","permissions","owner","group","modified"],"properties":{"group":{"type":"string"},"isDirectory":{"type":"boolean"},"isFile":{"type":"boolean"},"isSymlink":{"type":"boolean"},"modified":{"type":"string"},"owner":{"type":"string"},"permissions":{"type":"string","description":"Octal mode, e.g. `0644`."},"size":{"type":"integer","format":"int64","description":"Size in bytes.","minimum":0}}},"FirewallAction":{"type":"string","description":"What a rule does with the traffic it matches.\n\nOnly `allow` exists today, and it is required on every rule rather than\ndefaulted: a default would mean that the day `deny` arrives, every rule\nwritten before it silently acquires an action it never stated.","enum":["allow"]},"FirewallDecision":{"type":"object","description":"Why a connection would be allowed, or that it would not.\n\n`outcome` is `allowedByPlatform` (Freestyle's own ingress, e.g. a mapped\ndomain — no rule involved), `allowedByRule` (with the `ruleId` that\nmatched), `deniedByPlatform` (with the `reason`, e.g. `outboundMail`, that\nno rule can override), or `denied`.","required":["outcome"],"properties":{"outcome":{"type":"string"},"reason":{"type":["string","null"],"description":"Which platform policy closed it, on `deniedByPlatform`."},"ruleId":{"type":["string","null"],"description":"The rule that matched, on `allowedByRule`."},"side":{"type":["string","null"],"description":"Which end was Freestyle's ingress, on `allowedByPlatform`."}}},"FirewallDependency":{"type":"object","description":"A resource a rule cannot outlive: deleting it deletes the rule.","required":["kind","id"],"properties":{"displayName":{"type":["string","null"],"description":"Your label for the named resource, under the same rules as `slug`."},"id":{"type":"string","description":"The resource's id."},"kind":{"type":"string","description":"`vm`, `vpc`, or `tunnel` today. New selectors add new kinds."},"slug":{"type":["string","null"],"description":"Your handle for the named resource, when it has one — so a rule says\nwhat it points at without a lookup per id."}}},"FirewallEndpoint":{"type":"object","description":"One end of a rule: which traffic this side matches.\n\nFields intersect — `{ vpcId, port, protocol }` is \"traffic in that network,\non that port, over that protocol\". `vmId`/`vpcId`/`tunnelId` select by\nidentity (the VM however it is addressed); `cidr`/`public` select by\naddress. Unknown fields\nare rejected rather than ignored: a matcher is a security statement, and a\ntypo'd `porrt: 22` that quietly became \"every port\" is the wrong failure.","properties":{"cidr":{"type":["string","null"],"description":"An address range, IPv4 or IPv6, in canonical `network/prefix` form."},"port":{"type":["integer","null"],"format":"int32","description":"A single port, 1–65535. Requires `protocol`.","minimum":0},"protocol":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FirewallProtocol"}]},"public":{"type":["boolean","null"],"description":"Every publicly routable address, whoever owns it — including another\nFreestyle VM reached at its public address. Only `true` is meaningful;\nomit the field otherwise. Not combined with `vmId` or `vpcId`."},"tunnelId":{"type":["string","null"],"description":"Whatever is on the far side of one tunnel — the client that dials it,\nand any remote range routed over it. Tunnels have no slug, so this is a\ntunnel id: `tun-…`, or `wg-…` for one carried over from the per-VPC VPN\nrecords tunnels replaced."},"vmId":{"type":["string","null"],"description":"A single VM, by id or by your slug for it. Answers hold its id."},"vpcId":{"type":["string","null"],"description":"Everything on one private network, by id or by your slug for it.\nAnswers hold its id."}},"additionalProperties":false},"FirewallProtocol":{"type":"string","description":"The transport a matcher is restricted to.","enum":["tcp","udp","icmp"]},"FirewallRule":{"type":"object","description":"An allowed path through the network, as intent: traffic matching `source`\nmay reach `destination`.","required":["id","action","source","destination","createdAt","updatedAt"],"properties":{"action":{"$ref":"#/components/schemas/FirewallAction"},"createdAt":{"type":"string","format":"date-time"},"dependencies":{"type":"array","items":{"$ref":"#/components/schemas/FirewallDependency"},"description":"What this rule depends on. Deleting any of them deletes this rule, so\nno rule is ever left pointing at something that no longer exists."},"description":{"type":["string","null"]},"destination":{"$ref":"#/components/schemas/FirewallEndpoint"},"id":{"type":"string"},"source":{"$ref":"#/components/schemas/FirewallEndpoint"},"updatedAt":{"type":"string","format":"date-time"}}},"FirewallSpec":{"type":"object","description":"The `firewall` block of a create: rules created with the resource and\ndeleted with it. Inside it, an endpoint with no identity means the resource\nbeing created — which is how a rule names something whose id does not exist\nyet.","properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/CreateFirewallRuleRequest"}}},"additionalProperties":false},"FreestyleIdentity":{"type":"object","required":["id","managed"],"properties":{"id":{"$ref":"#/components/schemas/IdentityId"},"managed":{"type":"boolean"}}},"FreestyleIdentityInfo":{"type":"object","required":["id","accountId","managed"],"properties":{"accountId":{"$ref":"#/components/schemas/AccountId"},"id":{"$ref":"#/components/schemas/IdentityId"},"managed":{"type":"boolean"}}},"GrantVmPermissionBody":{"type":"object","required":["vmId"],"properties":{"allowedLinuxUsers":{"type":["array","null"],"items":{"type":"string"},"description":"Restrict the grant to these Linux users; omit (or null) for\nunrestricted access."},"vmId":{"$ref":"#/components/schemas/VmId"}}},"HardwarePowerAction":{"type":"string","description":"What to do to a machine's power.\n\nThe unit is always the whole machine. A machine with redundant supplies\ndraws from several cords, and Freestyle switches all of them together —\nthere is no way to ask for one, because cutting one cord browns a machine\nout rather than powering it off.","enum":["cycle","on","off"]},"HardwarePowerOutcome":{"type":"object","required":["action","applied","remainingToday"],"properties":{"action":{"$ref":"#/components/schemas/HardwarePowerAction"},"applied":{"type":"boolean"},"nextAllowedAt":{"type":["string","null"]},"remainingToday":{"type":"integer","format":"int32","minimum":0}}},"HardwarePowerRequest":{"type":"object","required":["action"],"properties":{"action":{"$ref":"#/components/schemas/HardwarePowerAction"}}},"HardwarePowerState":{"type":"object","description":"A machine's power, and what is left of its rate limit.","required":["supported","remainingToday"],"properties":{"cords":{"type":["integer","null"],"format":"int32","description":"How many power cords feed it — why control is all-or-nothing.","minimum":0},"lastActionAt":{"type":["string","null"]},"message":{"type":["string","null"],"description":"Why the power state is unknown, when it is."},"nextAllowedAt":{"type":["string","null"],"description":"When the next power-cutting action will be accepted. Absent means now."},"on":{"type":["boolean","null"],"description":"Every cord is live. Absent when the power state could not be read —\nwhich is not the same as the machine being off."},"remainingToday":{"type":"integer","format":"int32","description":"Power-cutting actions left in the rolling 24-hour budget. Turning power\n**on** does not spend it.","minimum":0},"supported":{"type":"boolean","description":"False when power control is not enabled for this machine; every other\nfield is then absent and every action is refused."},"watts":{"type":["number","null"],"format":"double","description":"Present draw across the machine's cords, in watts."}}},"HardwareServer":{"type":"object","description":"A physical machine attached to one of your private networks.\n\nFreestyle racks and cables these, so there is no create or delete here —\nwhat the API offers is seeing them and, where it is enabled, controlling\ntheir power.","required":["id","vpcId","ipv6","attached","dhcpBound","createdAt"],"properties":{"attached":{"type":"boolean","description":"The network attachment is converged and the machine is being served."},"createdAt":{"type":"string"},"dhcpBound":{"type":"boolean","description":"The machine has taken a DHCP lease — the closest thing to \"it is on\"\nthat the network can see by itself."},"displayName":{"type":["string","null"]},"id":{"type":"string"},"ipv4":{"type":["string","null"],"description":"Its address on the private network, when that network carries IPv4."},"ipv6":{"type":"string","description":"Its address on the private network. Every network has IPv6."},"lastSeen":{"type":["string","null"],"description":"When the network last saw traffic from it."},"mac":{"type":["string","null"],"description":"The machine's MAC, once it is known."},"powerControl":{"type":"boolean","description":"Whether this machine's power can be switched through the API. Off\nunless Freestyle has enabled it for the machine."},"publicIpv6":{"type":["string","null"],"description":"Its stable public address, when it was given one."},"vpcId":{"type":"string","description":"The private network the machine is attached to."}}},"HttpPathMatch":{"type":"object","description":"Exact request path. Prefix and regex matching are not supported yet.","required":["exact"],"properties":{"exact":{"type":"string"}},"additionalProperties":false},"HttpRequestMatch":{"type":"object","description":"Select which HTTP egress requests receive a rule's transforms.\nIncluded dimensions are ANDed; methods are ORed. An empty object matches all.\nA non-match skips every transform, without denying access to the origin.\nSee <https://vercel.com/docs/sandbox/concepts/firewall#matchers>.","properties":{"method":{"type":["array","null"],"items":{"type":"string"},"description":"Case-sensitive HTTP method tokens. Omitted matches any; an empty list matches none."},"path":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/HttpPathMatch","description":"Case-sensitive path comparison, excluding the query string."}]}},"additionalProperties":false},"IdentityId":{"type":"string","description":"Identifies an identity (the auth principal under an account). Minted\nas `\"idn-<uuid>\"`."},"IdentityTokenId":{"type":"string","description":"Identifies an identity access token row. Minted as `\"tok-<uuid>\"`."},"JsonPatch":{"type":"array","items":{"$ref":"#/components/schemas/PatchOperation"},"description":"Ordered RFC 6902 operations. Values are write-only and read back as `\"***\"`.\nA rule accepts one patch, with at most 128 operations and 64 KiB of configuration."},"LinuxUserId":{"type":"string","description":"Branded POSIX-style Linux username carried as `linuxUser` in JSON bodies\nand query strings. Constructed only via\n`try_from`/`from_str`, which enforces the same rule as\n`freestyle-firecracker/models/src/users.rs::is_valid_posix_name`:\n`[a-z_][a-z0-9_-]*` up to `MAX_LEN` chars.\n\nValidating at the type boundary keeps shell metacharacters and oversized\nvalues out of the host node and prevents them from reaching the guest."},"ListCertificatesResponse":{"type":"object","required":["certificates"],"properties":{"certificates":{"type":"array","items":{"$ref":"#/components/schemas/CertificateInfo"}}}},"ListDomainsResponse":{"type":"object","required":["domains"],"properties":{"domains":{"type":"array","items":{"$ref":"#/components/schemas/DomainOwnership"}}}},"ListFirewallRulesResponse":{"type":"object","required":["rules","totalCount"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/FirewallRule"},"description":"Newest first."},"totalCount":{"type":"integer","minimum":0}}},"ListHardwareResponse":{"type":"object","required":["hardwareServers","totalCount"],"properties":{"hardwareServers":{"type":"array","items":{"$ref":"#/components/schemas/HardwareServer"}},"totalCount":{"type":"integer","minimum":0}}},"ListIdentities":{"type":"object","required":["identities","total"],"properties":{"identities":{"type":"array","items":{"$ref":"#/components/schemas/FreestyleIdentity"}},"total":{"type":"integer","format":"int64","minimum":0}}},"ListPtySessionsResponse":{"type":"object","required":["sessions"],"properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/PtySession"}}}},"ListSnapshotsResponse":{"type":"object","required":["snapshots","totalCount"],"properties":{"snapshots":{"type":"array","items":{"$ref":"#/components/schemas/Snapshot"}},"totalCount":{"type":"integer","minimum":0}}},"ListTlsForwardAuthResponse":{"type":"object","required":["configs","totalCount"],"properties":{"configs":{"type":"array","items":{"$ref":"#/components/schemas/TlsForwardAuth"},"description":"Newest first."},"totalCount":{"type":"integer","minimum":0}}},"ListTlsRulesResponse":{"type":"object","required":["rules","totalCount"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/TlsRule"},"description":"Newest first."},"totalCount":{"type":"integer","minimum":0}}},"ListTunnelsResponse":{"type":"object","required":["tunnels","totalCount"],"properties":{"totalCount":{"type":"integer","minimum":0},"tunnels":{"type":"array","items":{"$ref":"#/components/schemas/Tunnel"},"description":"Newest first. Listings never carry a private key; the config returned\nhere has `PrivateKey` blanked."}}},"ListVerificationsResponse":{"type":"object","required":["verifications"],"properties":{"verifications":{"type":"array","items":{"$ref":"#/components/schemas/DomainVerification"}}}},"ListVmsResponse":{"type":"object","required":["vms","totalCount","runningCount","startingCount","pausedCount","stoppedCount"],"properties":{"pausedCount":{"type":"integer","minimum":0},"pausingCount":{"type":"integer","minimum":0},"runningCount":{"type":"integer","minimum":0},"startingCount":{"type":"integer","minimum":0},"stoppedCount":{"type":"integer","minimum":0},"totalCount":{"type":"integer","minimum":0},"vms":{"type":"array","items":{"$ref":"#/components/schemas/Vm"}}}},"ListVpcIpsResponse":{"type":"object","required":["ips","totalCount"],"properties":{"ips":{"type":"array","items":{"$ref":"#/components/schemas/VpcIpReservation"}},"totalCount":{"type":"integer","minimum":0}}},"ListVpcsResponse":{"type":"object","required":["vpcs","totalCount"],"properties":{"totalCount":{"type":"integer","minimum":0},"vpcs":{"type":"array","items":{"$ref":"#/components/schemas/Vpc"}}}},"MakeDirRequest":{"type":"object","description":"The public mkdir body. The internal API takes a bare `{path}` here while its\nsibling `remove` takes a query parameter; the public surface keeps the same\nwire shapes but documents both explicitly.","required":["path"],"properties":{"path":{"type":"string","description":"Absolute path inside the guest."}}},"MoveOperation":{"type":"object","description":"JSON Patch 'move' operation representation","required":["from","path"],"properties":{"from":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nto move value from."},"path":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nwithin the target document where the operation is performed."}}},"NetworkRoute":{"type":"object","description":"An in-guest static route: reach `cidr` via the gateway `via`, which must be\nan address inside the network's CIDR.","required":["cidr","via"],"properties":{"cidr":{"type":"string"},"metric":{"type":["integer","null"],"format":"int32","description":"Route priority, as `ip route`'s `metric`. Omit for the platform\ndefault. Several routes to the same `cidr` at the same metric are one\nmultipath route — the guest spreads flows across their gateways —\nwhile different metrics make a primary and a backup.","minimum":0},"via":{"type":"string"}}},"PatchOperation":{"oneOf":[{"allOf":[{"$ref":"#/components/schemas/AddOperation","description":"'add' operation"},{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["add"]}}}],"description":"'add' operation"},{"allOf":[{"$ref":"#/components/schemas/RemoveOperation","description":"'remove' operation"},{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["remove"]}}}],"description":"'remove' operation"},{"allOf":[{"$ref":"#/components/schemas/ReplaceOperation","description":"'replace' operation"},{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["replace"]}}}],"description":"'replace' operation"},{"allOf":[{"$ref":"#/components/schemas/MoveOperation","description":"'move' operation"},{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["move"]}}}],"description":"'move' operation"},{"allOf":[{"$ref":"#/components/schemas/CopyOperation","description":"'copy' operation"},{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["copy"]}}}],"description":"'copy' operation"},{"allOf":[{"$ref":"#/components/schemas/TestOperation","description":"'test' operation"},{"type":"object","required":["op"],"properties":{"op":{"type":"string","enum":["test"]}}}],"description":"'test' operation"}],"description":"JSON Patch single patch operation"},"PathExistsResponse":{"type":"object","required":["exists"],"properties":{"exists":{"type":"boolean"}}},"PlaceholderBranding":{"type":"object","description":"What your placeholder pages wear: the edge's 404 for a hostname under one of\nyour verified domains that none of your TLS rules serve.","required":["logoUrl","createdAt","updatedAt"],"properties":{"createdAt":{"type":"string","format":"date-time"},"logoUrl":{"type":"string","description":"An https URL of the logo. The visitor's browser loads it directly."},"productName":{"type":["string","null"],"description":"Named on the page in place of Freestyle. Null = logo only."},"updatedAt":{"type":"string","format":"date-time"}}},"PostgresTransform":{"type":"object","description":"Credentials injected into a Postgres startup handshake. The guest connects\nwith no password; the edge completes the real handshake upstream.","required":["username","password"],"properties":{"database":{"type":["string","null"],"description":"The database to select. Optional: omitted leaves the guest's choice."},"password":{"type":"string","description":"The upstream password. Write-only: sealed at rest, read back as `\"***\"`."},"username":{"type":"string","description":"The upstream role to authenticate as."}},"additionalProperties":false},"PtySession":{"type":"object","description":"One interactive terminal session. Sessions are owned by the guest agent and\noutlive the connection that opened them: dropping the WebSocket only\ndetaches, so the shell keeps running and a later client can reattach. An\nexited session stays listable and attachable briefly, so a client can still\nread the final output and exit code.","required":["sessionId","state","createdUnix","cols","rows"],"properties":{"cols":{"type":"integer","format":"int32","description":"Last-known terminal width, in columns.","minimum":0},"createdUnix":{"type":"integer","format":"int64","description":"Unix timestamp, in seconds, when the session was opened.","minimum":0},"exitCode":{"type":["integer","null"],"format":"int32","description":"Exit code, present only once the session has exited."},"linuxUser":{"type":["string","null"],"description":"Linux user the session runs as. Absent means the guest default, root."},"rows":{"type":"integer","format":"int32","description":"Last-known terminal height, in rows.","minimum":0},"sessionId":{"type":"integer","format":"int64","description":"Per-VM session id, for attaching or closing this session.","minimum":0},"slug":{"type":["string","null"],"description":"The name this session was opened with, while it still answers to it.\nReleased as soon as the shell exits, so an exited session reports none\nand is addressable only by `sessionId`."},"state":{"type":"string","description":"`running` while the shell is alive, `exited` once it has terminated."}}},"PublicErrorBody":{"type":"object","description":"The shape of every error response: a stable machine-readable `code` and a\nhuman-readable `message`. The HTTP status carries the class of failure.\n\nSome errors add fields alongside these two; `code` and `message` are always\npresent.","required":["code","message"],"properties":{"code":{"type":"string","description":"A stable identifier for the failure, in `SCREAMING_SNAKE_CASE`. Branch\non this; `message` is prose and may be reworded at any time.","example":"NOT_FOUND"},"message":{"type":"string","description":"A human-readable explanation of what went wrong. A fault inside the\nplatform is always reported as `INTERNAL_ERROR` with no further detail.","example":"vm vm-1234 not found"}}},"ReadDirResponse":{"type":"object","required":["entries"],"properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/DirEntry"}}}},"RemoveOperation":{"type":"object","description":"JSON Patch 'remove' operation representation","required":["path"],"properties":{"path":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nwithin the target document where the operation is performed."}}},"ReplaceOperation":{"type":"object","description":"JSON Patch 'replace' operation representation","required":["path","value"],"properties":{"path":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nwithin the target document where the operation is performed."},"value":{"description":"Value to replace with."}}},"RequestWildcardRequest":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"}}},"ResizeVmRequest":{"type":"object","properties":{"cpu":{"type":["integer","null"],"format":"int32","minimum":0},"memory":{"type":["integer","null"],"format":"int64","description":"Memory, MiB.","minimum":0},"storage":{"type":["integer","null"],"format":"int64","description":"Disk, MiB. Disks can only grow.","minimum":0}}},"RotateTunnelKeyRequest":{"type":"object","properties":{"clientPublicKey":{"type":["string","null"],"description":"The public key of a keypair you already hold. Omit to have a fresh one\nminted for you."}}},"S3Credentials":{"type":"object","required":["accessKeyId","secretAccessKey"],"properties":{"accessKeyId":{"type":"string"},"secretAccessKey":{"type":"string"},"sessionToken":{"type":["string","null"]}},"additionalProperties":false},"S3Operation":{"type":"string","enum":["GetObject","HeadObject","PutObject","DeleteObject","ListObjects","ListObjectsV2","CreateMultipartUpload","UploadPart","ListParts","CompleteMultipartUpload","AbortMultipartUpload","ListMultipartUploads"]},"S3Scope":{"type":"object","required":["bucket","prefix","operations"],"properties":{"bucket":{"type":"string"},"operations":{"type":"array","items":{"$ref":"#/components/schemas/S3Operation"}},"prefix":{"type":"string","description":"Literal UTF-8 key prefix, including a trailing slash for a directory boundary."}},"additionalProperties":false},"S3Transform":{"type":"object","required":["region","credentials","scope"],"properties":{"credentials":{"$ref":"#/components/schemas/S3Credentials"},"region":{"type":"string"},"scope":{"$ref":"#/components/schemas/S3Scope"}},"additionalProperties":false},"SetPlaceholderBrandingRequest":{"type":"object","required":["logoUrl"],"properties":{"logoUrl":{"type":"string","description":"An https URL of the logo, at most 2048 characters.","example":"https://cdn.example.com/logo.svg"},"productName":{"type":["string","null"],"description":"Named on the page in place of Freestyle, at most 64 characters. Omit\nfor logo only.","example":"Example Cloud"}}},"Snapshot":{"type":"object","description":"A snapshot as its owner sees it. The disk/memory layer tree, replication\ntargets, and originating manager are storage internals and are not exposed.","required":["id","createdAt","updatedAt"],"properties":{"autoDeleteFromPlan":{"type":"boolean","description":"Whether `autoDeleteSeconds` above is your plan's doing rather than\nyours, under the same rule as on a VM."},"autoDeleteSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the snapshot once this long has passed without a VM being\ncreated from it. Absent means never. Some plans cap this and the cap\nshows up here.","minimum":0},"createdAt":{"type":"string","format":"date-time"},"displayName":{"type":["string","null"]},"id":{"$ref":"#/components/schemas/SnapshotId"},"lastUsedAt":{"type":["string","null"],"format":"date-time","description":"When a VM was last created from this snapshot. Absent if none ever has\nbeen — which is not the same as never having been touched, since\nrenaming a snapshot is not using it."},"public":{"type":"boolean","description":"Public snapshots are bootable by any account."},"slug":{"type":["string","null"]},"sourceVmDisplayName":{"type":["string","null"],"description":"The source VM's display name now, under the same rules."},"sourceVmId":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/VmId"}]},"sourceVmSlug":{"type":["string","null"],"description":"What the source VM is called *now*; absent when it has no slug and\nwhen it is gone."},"ttlSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the snapshot this long after it was taken, however recently\nanything booted it.","minimum":0},"updatedAt":{"type":"string","format":"date-time"}}},"SnapshotCreated":{"type":"object","required":["snapshotId","snapshot"],"properties":{"snapshot":{"$ref":"#/components/schemas/Snapshot"},"snapshotId":{"$ref":"#/components/schemas/SnapshotId"},"sourceVmId":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/VmId"}]}}},"SnapshotId":{"type":"string","description":"Identifies a snapshot. Minted as `\"sh-<uuid>\"`."},"Socks5Transform":{"type":"object","description":"Credentials presented to an upstream SOCKS5 proxy. Your VM connects to the\nplatform's SOCKS5 front with no authentication at all; the edge authenticates\nto your provider on its behalf, so the subscription is never inside the VM.","required":["username","password"],"properties":{"password":{"type":"string","description":"The password to present to the upstream proxy. Write-only: sealed at\nrest, read back as `\"***\"`."},"username":{"type":"string","description":"The username to present to the upstream proxy."}},"additionalProperties":false},"TestOperation":{"type":"object","description":"JSON Patch 'test' operation representation","required":["path","value"],"properties":{"path":{"type":"string","description":"JSON-Pointer value [RFC6901](https://tools.ietf.org/html/rfc6901) that references a location\nwithin the target document where the operation is performed."},"value":{"description":"Value to test against."}}},"TlsAction":{"type":"string","description":"What a rule does with the named session it matches.\n\nOnly `allow` exists today, and it is required on every rule rather than\ndefaulted: a default would mean that the day `deny` arrives, every rule\nwritten before it silently acquires an action it never stated.","enum":["allow"]},"TlsDependency":{"type":"object","description":"A resource a rule cannot outlive: deleting it deletes the rule.","required":["kind","id"],"properties":{"displayName":{"type":["string","null"],"description":"Your label for the named resource, under the same rules as `slug`."},"id":{"type":"string","description":"The resource's id."},"kind":{"type":"string","description":"`vm` or `vpc` today. New selectors add new kinds."},"slug":{"type":["string","null"],"description":"Your handle for the named resource, when it has one — so a rule says\nwhat it points at without a lookup per id."}}},"TlsEndpoint":{"type":"object","description":"One end of a rule: which session this side matches.\n\nA `source` says *who* may open the session (`vmId`/`vpcId`/`public: true`); a\n`destination` says *where it lands* (`vmId`+`port`, `host`+`port`, or\n`public: true` for the domain's own origin). Unknown fields are rejected\nrather than ignored: a rule is a security statement, and a typo that quietly\nwidened it is the wrong failure.","properties":{"host":{"type":["string","null"],"description":"**Destination only.** Pin the session to this host instead of resolving\nthe domain, while still presenting `domain` as the SNI."},"port":{"type":["integer","null"],"format":"int32","description":"**Destination only.** The port the session lands on. Required with a\n`vmId` or `host` destination; defaults to 443 for a `public` origin.","minimum":0},"public":{"type":["boolean","null"],"description":"On a source, every publicly routable client — the open Internet dials the\nname. On a destination, the domain's own public origin. Only `true` is\nmeaningful; omit the field otherwise."},"vmId":{"type":["string","null"],"description":"A single VM, by id or by your slug for it. On a source, the VM that may\nopen the session; on a destination, the VM it lands on. Answers hold its id."},"vpcId":{"type":["string","null"],"description":"Everything on one private network, by id or by your slug for it. **Source\nonly** — a landing is one place, so a network cannot be a destination.\nAnswers hold its id."}},"additionalProperties":false},"TlsForwardAuth":{"type":"object","description":"A stored forward-auth configuration. Static header values are redacted.","required":["id","accountId","url","timeoutMs","redacted","createdAt","updatedAt"],"properties":{"accountId":{"type":"string"},"authResponseHeaders":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"headers":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"id":{"type":"string"},"protectedCookies":{"type":"array","items":{"type":"string"}},"redacted":{"type":"boolean"},"timeoutMs":{"type":"integer","format":"int32","minimum":0},"updatedAt":{"type":"string","format":"date-time"},"url":{"type":"string"}}},"TlsForwardAuthReference":{"type":"object","description":"A reusable authorization check attached to public HTTP ingress.","required":["id"],"properties":{"id":{"type":"string","description":"The configuration id returned by `POST /v5/tls/forward-auth`."}},"additionalProperties":false},"TlsProtocolDocumented":{"type":"string","description":"How the edge treats the session's bytes, and therefore which of its front\ndoors serves the rule.\n\n`http` (443) and `postgres` (5432) terminate the session, so each pairs with\nthe transform kind that speaks it and each needs a certificate for the name.\n`minecraft` (25565) routes on the server address in the client's handshake\nand then splices — no TLS, so no certificate and no transform.\n\nThe other two doors carry a terminating protocol and a splicing one at once,\nand a rule picks which it is. `tcp` shares 443 with `http`: match on SNI,\nsplice the bytes, terminate nothing, so your VM's own certificate answers the\nhandshake. `imap` and `imaps` share 993 for mail clients — `imap` terminates\non a certificate issued for the name and forwards plain IMAP to the port your\nserver listens on (143 by default), `imaps` splices to a server holding its\nown certificate on 993. All three splicing shapes are public ingress only.\n\n`socks5` (1080) goes the other way and is the only egress-only protocol. Your\nVM opens SOCKS5 to the platform edge with no authentication, names its own\ndestination inside the request, and the edge opens the session onward through\nyour proxy provider using the credentials a `socks5` transform carries — so\nthe workload can use the subscription without ever holding it. Nothing is\nterminated: your TLS runs end to end to the real origin. The rule's `domain`\nis an allow-list of destinations rather than a name anything resolves, which\nis why `*` is allowed here and refused everywhere else.","enum":["tcp","http","postgres","minecraft","imap","imaps","socks5"]},"TlsRule":{"type":"object","description":"An allowed named session over a domain, as intent: a session addressed to\n`domain`, from `source`, to `destination`, with any in-flight `transform`.","required":["id","action","domain","protocol","source","destination","createdAt","updatedAt"],"properties":{"action":{"$ref":"#/components/schemas/TlsAction"},"createdAt":{"type":"string","format":"date-time"},"dependencies":{"type":"array","items":{"$ref":"#/components/schemas/TlsDependency"},"description":"What this rule depends on. Deleting any of them deletes this rule, so no\nrule is ever left pointing at something that no longer exists."},"description":{"type":["string","null"]},"destination":{"$ref":"#/components/schemas/TlsEndpoint"},"domain":{"type":"string","description":"The name the session is addressed to: exact, `*.suffix`, or `*`."},"forwardAuth":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TlsForwardAuthReference","description":"Optional reusable authorization check for public HTTP ingress."}]},"id":{"type":"string"},"match":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/HttpRequestMatch","description":"Vercel-style method/exact-path selector for HTTP egress transforms.\nNon-matching requests pass through without any transform."}]},"protocol":{"$ref":"#/components/schemas/TlsProtocolDocumented"},"redacted":{"type":"boolean","description":"Whether any transform secret was redacted out of `transform`, so you know\nthe shape you see is not the whole story."},"source":{"$ref":"#/components/schemas/TlsEndpoint"},"transform":{"type":"array","items":{"$ref":"#/components/schemas/TlsTransformDocumented"},"description":"The rewrites this rule applies, with every secret replaced by `\"***\"`."},"updatedAt":{"type":"string","format":"date-time"}}},"TlsSpec":{"type":"object","description":"The `tls` block of a VM create: rules created with the VM and deleted with\nit. Inside it, an endpoint with no identity means the VM being created —\nwhich is how a rule names a VM whose id does not exist yet.","properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/CreateTlsRuleRequest"}}},"additionalProperties":false},"TlsTransformDocumented":{"oneOf":[{"type":"object","description":"Set request headers on an `http` session. Header names are preserved\n(they are not secret); values are write-only.","required":["headers"],"properties":{"headers":{"type":"object","description":"Set request headers on an `http` session. Header names are preserved\n(they are not secret); values are write-only.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}},{"type":"object","description":"Apply RFC 6902 operations to JSON HTTP egress request bodies.\nValues are write-only; operation names and JSON Pointer paths remain visible.","required":["jsonPatch"],"properties":{"jsonPatch":{"$ref":"#/components/schemas/JsonPatch","description":"Apply RFC 6902 operations to JSON HTTP egress request bodies.\nValues are write-only; operation names and JSON Pointer paths remain visible."}}},{"type":"object","description":"Inject credentials into a `postgres` handshake.","required":["postgres"],"properties":{"postgres":{"$ref":"#/components/schemas/PostgresTransform","description":"Inject credentials into a `postgres` handshake."}}},{"type":"object","description":"Authenticate a `socks5` session to your upstream proxy provider.","required":["socks5"],"properties":{"socks5":{"$ref":"#/components/schemas/Socks5Transform","description":"Authenticate a `socks5` session to your upstream proxy provider."}}},{"type":"object","description":"Authorize and sign scoped S3 HTTP requests at the edge.","required":["s3"],"properties":{"s3":{"$ref":"#/components/schemas/S3Transform","description":"Authorize and sign scoped S3 HTTP requests at the edge."}}}],"description":"The rewrites a rule applies, in order."},"TrafficDescription":{"type":"object","description":"A connection to decide about.","required":["source","destination"],"properties":{"destination":{"$ref":"#/components/schemas/TrafficParty"},"protocol":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FirewallProtocol"}]},"source":{"$ref":"#/components/schemas/TrafficParty"}},"additionalProperties":false},"TrafficParty":{"type":"object","description":"One end of a connection to ask about: everything true about the party, not\na pattern.","properties":{"address":{"type":["string","null"],"description":"The address in use, when there is one — what `cidr` rules are tested against."},"platform":{"type":"boolean","description":"Freestyle's own ingress — the web proxy serving a mapped domain, or the\nSSH proxy. Always allowed, whatever the rules say."},"port":{"type":["integer","null"],"format":"int32","minimum":0},"public":{"type":"boolean","description":"Whether that address is publicly routable — what `public: true` matches."},"tunnelId":{"type":["string","null"]},"vmId":{"type":["string","null"]},"vpcIds":{"type":"array","items":{"type":"string"},"description":"Every private network this party is on; a rule naming one of them matches."}},"additionalProperties":false},"Tunnel":{"type":"object","description":"A tunnel: your WireGuard identity on the platform. It exists until you\ndelete it: connect, disconnect and reconnect as often as you like — a\nsession is set up on the handshake that needs it, so an idle tunnel costs\nnothing and never expires. A tunnel routes nowhere until you attach a\nprivate network to it; attach several and one WireGuard interface reaches\nthem all.","required":["id","clientConfig","endpointPort","clientPublicKey","serverPublicKey","clientAddressV4","clientAddressV6","routes","attachments","createdAt","updatedAt"],"properties":{"attachments":{"type":"array","items":{"$ref":"#/components/schemas/TunnelAttachment"},"description":"The private networks on the far side, oldest attachment first."},"clientAddressV4":{"type":"string","description":"Your fixed addresses inside the tunnel. Only the gateway ever sees\nthem: each attached network sees an address inside its own subnet\ninstead (`attachments[].address`)."},"clientAddressV6":{"type":"string"},"clientConfig":{"type":"string","description":"A complete WireGuard config file, fixed for the life of the tunnel:\nattaching and detaching networks never changes it, so bring it up once\nand manage what it reaches through the API. `PrivateKey` is blank\nexcept on create and rotate-key, the only two responses that mint a\nkeypair."},"clientPublicKey":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"displayName":{"type":["string","null"],"description":"Your label for this tunnel, if you gave it one."},"endpointHost":{"type":["string","null"],"description":"Host to dial."},"endpointPort":{"type":"integer","format":"int32","minimum":0},"id":{"type":"string","description":"Deprecated: the tunnel's id under its old name. Read `tunnelId`."},"routes":{"type":"array","items":{"type":"string"},"description":"The ranges your client routes through the tunnel — its `AllowedIPs`,\nfixed at create. A network can only be attached if its CIDRs fall\ninside these."},"serverPublicKey":{"type":"string","description":"The one `[Peer]` public key in your config."},"slug":{"type":["string","null"],"description":"Your handle for this tunnel, if you gave it one. Usable anywhere the id\nis, in place of it."},"tunnelId":{"type":["string","null"],"description":"The tunnel's id, under the name the rest of the API uses for it — a\nfirewall rule says `tunnelId`, so the tunnel does too."},"updatedAt":{"type":"string","format":"date-time"}}},"TunnelAttachment":{"type":"object","description":"One private network on the far side of a tunnel: the network, your address\ninside it, and the `[Peer]` key that carries it.","required":["vpcId","address","vpcCidr","allowedIps","createdAt"],"properties":{"address":{"type":"string","description":"Deprecated: the primary address, whichever family. Read `ipv4`/`ipv6`."},"allowedIps":{"type":"array","items":{"type":"string"},"description":"The CIDRs the gateway routes to this network."},"createdAt":{"type":"string","format":"date-time"},"exit":{"type":"boolean","description":"Whether this attachment is a network exit: your client forwards for\ndestinations that are not its own address, and members route through\nit with an ordinary route via the attachment's address. Omitted when\nfalse."},"ipv4":{"type":["string","null"],"description":"Your IPv4 address inside the private network, absent on a v6-only\nattachment. It belongs to this tunnel for as long as the network stays\nattached, so you can write it into configs and rules."},"ipv6":{"type":["string","null"],"description":"Your IPv6 address inside the private network, under the same rules."},"remoteCidrs":{"type":"array","items":{"type":"string"},"description":"Ranges behind your client that this network routes through the tunnel\n(a site-to-site grant). VMs reach them with an ordinary route via\n`address`. Omitted when the attachment has none."},"secondaryAddress":{"type":["string","null"],"description":"Deprecated: the second-family address. Read `ipv4`/`ipv6`."},"vpcCidr":{"type":"string"},"vpcId":{"$ref":"#/components/schemas/VpcId"}}},"UpdateAllowedLinuxUsersBody":{"type":"object","properties":{"allowedLinuxUsers":{"type":["array","null"],"items":{"type":"string"},"description":"The new allowed-Linux-users list; null grants unrestricted access."}}},"UpdateSnapshotRequest":{"type":"object","properties":{"autoDeleteSeconds":{"type":["integer","null"],"format":"int64","description":"Seconds of nothing being created from it before the snapshot is\ndeleted; -1 removes the window. On a plan that caps it, -1 puts you\nback on the cap rather than off it."},"displayName":{"type":["string","null"]},"slug":{"type":["string","null"]},"ttlSeconds":{"type":["integer","null"],"format":"int64","description":"Seconds from when the snapshot was taken — not from now — before it is\ndeleted; -1 removes the deadline."}},"additionalProperties":false},"UpdateTunnelRequest":{"type":"object","properties":{"displayName":{"type":["string","null"],"description":"An empty string clears it."},"slug":{"type":["string","null"],"description":"An empty string clears it."}}},"UpdateVmNetworksRequest":{"type":"object","description":"The desired network set for `PUT /vms/{vmIdOrSlug}/networks`. Declarative:\nwhat is listed is attached, what is absent is detached.","properties":{"networks":{"type":"array","items":{"$ref":"#/components/schemas/AttachNetwork"},"description":"At most one network is accepted today. An empty list detaches the VM.\nAlso accepted as `vpcs`, the name the rest of the API uses."}}},"UpdateVmRequest":{"type":"object","properties":{"autoDeleteSeconds":{"type":["integer","null"],"format":"int64","description":"Seconds of not running before the VM is deleted; -1 removes the window\n(on a plan that caps it, -1 puts you back on the cap rather than off\nit), and 0 makes the VM ephemeral — deleted the moment it stops, or\nright away if it is already stopped."},"automaticRestart":{"type":["boolean","null"],"description":"Turn automatic restart on or off. Applies to the next failure: this\nneither boots a stopped VM nor stops a running one."},"displayName":{"type":["string","null"],"description":"Change the display label; an empty string clears it."},"idleTimeoutSeconds":{"type":["integer","null"],"format":"int64","description":"Seconds of network idleness before pause; -1 removes the timeout."},"maxRunSeconds":{"type":["integer","null"],"format":"int64","description":"Seconds one run may last before the VM is paused; -1 removes the cap."},"maxRunTotalSeconds":{"type":["integer","null"],"format":"int64","description":"The lifetime runtime budget in seconds; -1 removes it. Raising this\npast `totalRunSeconds` is how you start a VM that spent its budget."},"metadata":{"type":["object","null"],"description":"Metadata merged into the VM. Only the keys you send are touched; send an\nempty value to remove one.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"reassignSlug":{"type":"boolean","description":"Take `slug` even if another VM in this account already holds it. The\nslug moves atomically: the current holder keeps running, stays\naddressable by id, and just loses its slug. Without this flag a taken\nslug is a 409 CONFLICT. Requires a non-empty `slug`."},"slug":{"type":["string","null"],"description":"Change the slug; an empty string clears it."},"ttlSeconds":{"type":["integer","null"],"format":"int64","description":"Seconds from creation — not from now — before the VM is deleted; -1\nremoves the deadline. A value already in the past deletes it shortly."}}},"UpdateVpcRequest":{"type":"object","properties":{"displayName":{"type":["string","null"],"description":"An empty string clears it."},"slug":{"type":["string","null"],"description":"An empty string clears it."}}},"UploadSessionStatus":{"type":"object","required":["uploadId","path","size","receivedBytes"],"properties":{"path":{"type":"string","description":"Absolute guest path the file will be committed to."},"receivedBytes":{"type":"integer","format":"int64","description":"Bytes stored so far — the offset the next chunk must start at.","minimum":0},"size":{"type":"integer","format":"int64","description":"Total declared file size in bytes.","minimum":0},"uploadId":{"type":"string"}}},"VerificationState":{"type":"string","description":"Where a challenge stands. A challenge is not consumed by success: it\nbecomes the durable record of which user proved control.","enum":["pending","verified"]},"Vm":{"type":"object","description":"A VM as the owning account sees it. Placement and topology internals\n(hosting manager, VXLAN ids, node-local network allocation) are not part of\nthis contract.","required":["id","state","resources","createdAt","updatedAt"],"properties":{"autoDeleteFromPlan":{"type":"boolean","description":"Whether `autoDeleteSeconds` above is your plan's doing rather than\nyours. A window your plan set goes away by itself if you move to a plan\nthat does not reclaim unused VMs; one you set is yours and stays."},"autoDeleteSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the VM once it has gone this long without running. Absent means\nnever; 0 means the VM is ephemeral — deleted the moment it stops. Note\nthat this may be set for you: some plans cap how long an unused VM is\nkept, and the cap shows up here.","minimum":0},"automaticRestart":{"type":["boolean","null"],"description":"Whether the VM is booted again when it stops without being asked to."},"cpuTimeSeconds":{"type":["number","null"],"format":"double","description":"Total CPU time consumed, seconds."},"createdAt":{"type":"string","format":"date-time"},"displayName":{"type":["string","null"]},"egressIpv4":{"type":["string","null"],"description":"The public IPv4 address the VM's outbound traffic is seen as coming\nfrom. It belongs to your account rather than to this VM: every VM you\nown shares it, and it survives restarts and moves between machines.\n\nOptional, and worth reading as optional even though a VM in service\nhas one: nothing promises an account keeps a single outbound address,\nso allow for it being absent rather than depending on it being there."},"egressIpv6":{"type":["string","null"],"description":"The public IPv6 address the VM's outbound traffic is seen as coming\nfrom. Today it is the VM's own `publicIpv6` — it appears from the\naddress it answers on — but read it as its own field: where a VM\nanswers and where it appears from are two questions, and `egressIpv4`\nabove already answers them differently."},"id":{"$ref":"#/components/schemas/VmId"},"idleTimeoutSeconds":{"type":["integer","null"],"format":"int64","minimum":0},"image":{"type":["string","null"],"description":"The image the VM booted from, when it booted from one."},"lastNetworkActivity":{"type":["string","null"],"format":"date-time"},"maxRunSeconds":{"type":["integer","null"],"format":"int64","description":"Pause the VM once one run has lasted this long, however busy it is.","minimum":0},"maxRunTotalSeconds":{"type":["integer","null"],"format":"int64","description":"The VM's lifetime runtime budget in seconds. Spending it pauses the VM\nand makes every later start a 409 until you raise this.","minimum":0},"metadata":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"networks":{"type":"array","items":{"$ref":"#/components/schemas/VmNetwork"},"description":"Deprecated: the same list as `vpcs`, kept for old readers."},"placement":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/VmPlacement","description":"The placement constraints the VM was created with, when any."}]},"publicIpv6":{"type":["string","null"],"description":"The VM's stable public IPv6 address — assigned at create and kept for\nthe VM's life, across restarts and moves between machines."},"resources":{"$ref":"#/components/schemas/VmResources"},"slug":{"type":["string","null"],"description":"URL-safe identifier, unique within the account; usable anywhere an id is."},"snapshotId":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SnapshotId","description":"The snapshot the VM booted from, when it booted from one."}]},"sourceSnapshotDisplayName":{"type":["string","null"],"description":"The snapshot's display name now, under the same rules."},"sourceSnapshotSlug":{"type":["string","null"],"description":"What that snapshot is called *now* — its own slug, unqualified. Absent\nwhen it has no slug and when it is gone, so read against\n`sourceSnapshotSlugAtCreate` a renamed origin tells apart from a\ndeleted one."},"sourceSnapshotSlugAtCreate":{"type":["string","null"],"description":"What that snapshot was called when the VM was created, qualified\n`{owner}/{slug}` when it belongs to another account. A name, not a\npointer: slugs move, so boot from `snapshotId`, not from this."},"state":{"$ref":"#/components/schemas/VmState"},"totalRunSeconds":{"type":"integer","format":"int64","description":"Seconds this VM has spent running, across every run it has ever had.\nWhat `maxRunTotalSeconds` is measured against.","minimum":0},"ttlSeconds":{"type":["integer","null"],"format":"int64","description":"Delete the VM this long after it was created, whatever it is doing.","minimum":0},"updatedAt":{"type":"string","format":"date-time"}}},"VmAntiAffinityRule":{"type":"object","description":"One anti-affinity rule: \"not on the same `topology` domain as any VM\nmatching `selector`\". At most 8 rules per VM.","required":["topology","selector"],"properties":{"selector":{"$ref":"#/components/schemas/VmLabelSelector"},"topology":{"$ref":"#/components/schemas/VmPlacementTopology"}}},"VmId":{"type":"string","description":"Identifies a VM. Minted as `\"vm-<uuid>\"`."},"VmLabelSelector":{"type":"object","description":"Selects VMs by their metadata: a VM matches when its metadata contains\nevery `matchLabels` entry. At least 1 and at most 8 entries; keys and\nvalues are limited to 63 characters, like the metadata they match.","required":["matchLabels"],"properties":{"matchLabels":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}},"VmNetwork":{"type":"object","description":"A VM's place on a private network.","required":["vpc"],"properties":{"cidr":{"type":["string","null"],"description":"The network's CIDR, alongside `ipv4`."},"cidrV6":{"type":["string","null"],"description":"The network's IPv6 CIDR, alongside `ipv6`."},"ipv4":{"type":["string","null"],"description":"The VM's IPv4 address inside the network; absent when the member opted\nout of IPv4, or the network has no IPv4 CIDR."},"ipv6":{"type":["string","null"],"description":"The VM's IPv6 address, when the network has an IPv6 CIDR."},"routes":{"type":"array","items":{"$ref":"#/components/schemas/NetworkRoute"}},"vpc":{"$ref":"#/components/schemas/VpcId","description":"Deprecated: the network's id under its old name. Read `vpcId`."},"vpcDisplayName":{"type":["string","null"],"description":"The network's display name now, under the same rules."},"vpcId":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/VpcId","description":"The network's id, under the name the rest of the API uses. Filled on\nthe way out."}]},"vpcSlug":{"type":["string","null"],"description":"What the network is called *now*. Absent when it has no slug and when\nit is gone, so read against `vpcSlugAtAttach` a renamed network tells\napart from a deleted one."},"vpcSlugAtAttach":{"type":["string","null"],"description":"Your slug for the network when the VM was attached to it, if it had one.\n\nA convenience for reading an answer back — `vpc` is the id, and the id is\nwhat to store or compare. It keeps naming what the VM was attached by\nafter a rename, so this is not a way to find the network again."}}},"VmPermission":{"type":"object","required":["id","identityId","vmId","grantedAt","grantedBy"],"properties":{"allowedLinuxUsers":{"type":["array","null"],"items":{"type":"string"}},"grantedAt":{"type":"string","format":"date-time"},"grantedBy":{"$ref":"#/components/schemas/AccountId"},"id":{"$ref":"#/components/schemas/VmPermissionId"},"identityId":{"$ref":"#/components/schemas/IdentityId"},"vmId":{"$ref":"#/components/schemas/VmId"}}},"VmPermissionId":{"type":"string","description":"Identifies a VM-permission grant row. Minted as `\"vmp-<uuid>\"`."},"VmPlacement":{"type":"object","description":"Placement constraints for a VM, fixed at create time and honored again\nwhenever the platform relocates the VM.","properties":{"antiAffinity":{"type":"array","items":{"$ref":"#/components/schemas/VmAntiAffinityRule"},"description":"Hard rules: the VM will not land on a topology domain already hosting\na VM matched by any rule's selector."}}},"VmPlacementTopology":{"type":"string","description":"The topology domain an anti-affinity rule spreads across. `node` — the\nonly domain today — means \"not on the same host machine\".","enum":["node"]},"VmResources":{"type":"object","required":["cpu","memory","storage"],"properties":{"cpu":{"type":"integer","format":"int32","description":"vCPU count.","minimum":0},"memory":{"type":"integer","format":"int64","description":"Memory, MiB.","minimum":0},"storage":{"type":"integer","format":"int64","description":"Disk, MiB.","minimum":0}}},"VmState":{"type":"string","enum":["starting","running","pausing","paused","stopped"]},"Vpc":{"type":"object","description":"A private network. Its VXLAN id is a cluster internal and is not published.","required":["id","cidrV6","createdAt"],"properties":{"cidr":{"type":["string","null"],"description":"The network's IPv4 CIDR block, when it has one. Networks are\nIPv6-first; IPv4 is opt-in at create."},"cidrV6":{"type":"string","description":"The network's IPv6 CIDR block — always present."},"createdAt":{"type":"string","format":"date-time"},"displayName":{"type":["string","null"]},"id":{"$ref":"#/components/schemas/VpcId"},"slug":{"type":["string","null"]}}},"VpcId":{"type":"string","description":"Identifies a VPC. Minted as `\"vpc-<uuid>\"`."},"VpcIpReservation":{"type":"object","description":"One reserved address inside a private network.","required":["ipv4","owner"],"properties":{"ipv4":{"type":"string"},"owner":{"type":"string","description":"Who holds the address: a VM id, or `wireguard:{attachmentId}` for a\ntunnel attachment's address."}}},"WriteFileRequest":{"type":"object","description":"The JSON form of a write: convenient from a docs page or a shell, capped at\n32 MiB. Larger files go as raw bytes on the same endpoint.","required":["path","content"],"properties":{"content":{"type":"string","description":"The file's content, interpreted per `encoding`."},"encoding":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ContentEncoding","description":"Defaults to `utf8`."}]},"mode":{"type":["integer","null"],"format":"int32","description":"Final file mode bits (e.g. 493 for `0o755`).","minimum":0},"path":{"type":"string","description":"Absolute path inside the guest."},"sha256":{"type":["string","null"],"description":"Optional sha256 of the file as 64 hex characters."}}}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Your Freestyle API key, sent as `Authorization: Bearer <api key>`. Every request needs one unless it is authenticated with an identity access token instead."},"identityAccessToken":{"type":"apiKey","in":"header","name":"x-freestyle-identity-access-token","description":"An access token belonging to an identity you created. It reaches only the VMs that identity has been granted, and only their exec and terminal routes. Hand one to an end user instead of your API key."}}},"security":[{"apiKey":[]}],"tags":[{"name":"VMs","description":"Boot, control, and run commands in your VMs"},{"name":"Filesystem","description":"Read and write files inside a running VM"},{"name":"Snapshots","description":"Capture a VM's exact state, memory and disk, and boot new VMs from it"},{"name":"VPCs","description":"Private networks your VMs can share"},{"name":"Domains","description":"Point your own domains at a VM, with TLS"},{"name":"Identities","description":"Scoped access for your end users: identities, tokens, and per-VM grants"},{"name":"Background requests","description":"Run any call server-side and poll for its result: send `x-freestyle-background-after-secs: <n>` on the original request, and if it outlives that window you get a `202` with a request id to poll here. The work is never cancelled by the client disconnecting."}]}