A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Fly.io API is how an app or AI agent runs infrastructure on Fly.io: creating and deleting apps, launching virtual machines from a Docker image, starting, stopping, and restarting them, and attaching persistent volumes. Access is granted through an API token sent as a bearer credential, and that token is scoped either to one app or to a whole organization, which sets the apps a call can reach. The API does not push events, so an agent that needs to know when a machine changes state waits on the machine or polls it.
How an app or AI agent connects to Fly.io determines what it can reach. There is one route for managing infrastructure, the Machines REST API, governed by the API token behind it and the apps that token is scoped to.
The Machines REST API answers at https://api.machines.dev/v1 over the public internet, and at an internal address inside the Fly.io private network. It provisions and manages apps, Machines, volumes, and secrets.
Fly.io ships a first-party MCP server inside flyctl, run with 'fly mcp server'. It exposes apps, Machines, volumes, secrets, logs, and status to an AI agent, defaults to a local stdio connection bound to localhost, and can be exposed remotely with the stream or sse options.
A separate GraphQL API covers organization and billing operations that the Machines REST API does not. The Machines REST API is the focus for managing apps and Machines.
A deploy token, made with 'fly tokens create deploy', is restricted to a single app and everything in it. It is the narrowest practical token for an automation that touches one app, and is the recommended choice for sharing with a third party such as a CI pipeline.
An org token, made with 'fly tokens create org', can manage every app in one organization, including creating new apps. It sits between the broad account token and the restricted app token.
flyctl can mint tighter tokens, such as a machine-exec token that only runs commands in a Machine, a readonly token that cannot make changes, and an ssh token. The narrowest scope that does the job is the recommended choice.
The Machines API is split into areas an agent can act on, such as apps, the Machines inside them, persistent volumes, and per-app secrets. Each area has its own methods, and some change live running infrastructure while others only read it.
List the apps in an organization, read a single app, create an app to group Machines, and delete an app and everything in it.
List, read, create, update, and delete the virtual machines inside an app.
Start, stop, restart, suspend, signal, wait on, and run commands inside a Machine.
Read a Machine's running processes, its event history, and its past versions.
Acquire, read, and release an exclusive lock on a single Machine.
List, read, create, extend, and delete the persistent storage volumes attached to Machines.
List an app's secret names and set or remove a named secret that its Machines can read.
Request an OpenID Connect token a Machine can present to a third-party service.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
AppsList the apps in an organization, read a single app, create an app to group Machines, and delete an app and everything in it.4 | ||||||
| GET | /v1/apps?org_slug={org_slug} | List the apps in an organization. | read | Org token | Current | |
Listing across an organization needs an org-scoped token; an app-scoped deploy token sees only its own app. Acts onapp Permission (capability) Org tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name} | Get a single app's details. | read | App token | Current | |
An app-scoped deploy token is enough to read its own app. Acts onapp Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps | Create an app to group Machines under one name and organization. | write | Org token | Current | |
Creating an app needs an org-scoped token, since a new app belongs to the organization, not an existing app. Acts onapp Permission (capability) Org tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/apps/{app_name} | Delete an app, removing every Machine and volume inside it. | write | App token | Current | |
App deletions are separately rate-limited to 100 per minute. An app-scoped token can delete its own app. Acts onapp Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit100 per minute SourceOfficial documentation ↗ | ||||||
MachinesList, read, create, update, and delete the virtual machines inside an app.5 | ||||||
| GET | /v1/apps/{app_name}/machines | List the Machines in an app. | read | App token | Current | |
Reads are governed by which app the token reaches, not a separate permission. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name}/machines/{machine_id} | Get a single Machine's details and current state. | read | App token | Current | |
Get-Machine has a higher rate ceiling than other actions, at 5 per second with a burst to 10. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit5 req/s (burst 10) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines | Create and start a Machine from a Docker image and a config object. | write | App token | Current | |
The config sets the image, guest size, environment, mounts, and services. Writes are governed by the token's app scope. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id} | Update a Machine's configuration, replacing it with a new version. | write | App token | Current | |
Updating creates a new Machine version. A held lease nonce may be required to modify the Machine. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/apps/{app_name}/machines/{machine_id} | Delete a Machine. | write | App token | Current | |
A Machine usually has to be stopped first, or deleted with the force option. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
Machine state & controlStart, stop, restart, suspend, signal, wait on, and run commands inside a Machine.7 | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/start | Start a stopped or suspended Machine. | write | App token | Current | |
Governed by the token's app scope. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/stop | Stop a running Machine, with an optional signal and timeout. | write | App token | Current | |
The signal defaults to SIGINT, followed by a hard stop after the timeout. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/restart | Restart a Machine, stopping then starting it. | write | App token | Current | |
Takes an optional signal and a timeout in nanoseconds before a hard shutdown. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/suspend | Suspend a Machine, saving its memory state so it resumes quickly. | write | App token | Current | |
Suspend saves the Machine's memory to disk; starting it again restores that state. Enabled in all regions on 24 July 2024. Acts onmachine Permission (capability) App tokenVersionIntroduced 2024-07-24 Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/signal | Send a Unix signal to the Machine's main process. | write | App token | Current | |
Delivers a named signal, such as SIGTERM, to the running process. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/exec | Run a command inside a running Machine and return its output and exit code. | write | machine-exec token | Current | |
A machine-exec scoped token grants exactly this, without the power to create or delete infrastructure. Acts onmachine Permission (capability) machine-exec tokenVersionAvailable since the API’s base version Webhook eventNone Rate limit1 req/s (burst 3) SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name}/machines/{machine_id}/wait | Hold the request open until a Machine reaches a target state or the timeout passes. | read | App token | Current | |
Returns {"ok": true} once the Machine reaches started, stopped, suspended, or destroyed; a timeout returns 408. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Machine infoRead a Machine's running processes, its event history, and its past versions.3 | ||||||
| GET | /v1/apps/{app_name}/machines/{machine_id}/ps | List the processes running inside a Machine. | read | App token | Current | |
Read-only view of the Machine's live processes. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name}/machines/{machine_id}/events | List a Machine's recent state-change events. | read | App token | Current | |
The closest the API offers to a change feed, polled rather than pushed. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name}/machines/{machine_id}/versions | List a Machine's past configuration versions. | read | App token | Current | |
Each update creates a new version; this lists them. Acts onmachine Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
LeasesAcquire, read, and release an exclusive lock on a single Machine.3 | ||||||
| POST | /v1/apps/{app_name}/machines/{machine_id}/lease | Acquire an exclusive lease on a Machine and receive a nonce. | write | App token | Current | |
Returns 201 with a nonce; acquiring a lease on an already-leased Machine returns 409 Conflict. Acts onlease Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name}/machines/{machine_id}/lease | Get the current lease on a Machine. | read | App token | Current | |
Returns 404 when no lease is held. Acts onlease Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/apps/{app_name}/machines/{machine_id}/lease | Release the lease held on a Machine. | write | App token | Current | |
Releasing requires the held nonce in the fly-machine-lease-nonce header. Acts onlease Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
VolumesList, read, create, extend, and delete the persistent storage volumes attached to Machines.5 | ||||||
| GET | /v1/apps/{app_name}/volumes | List the volumes in an app. | read | App token | Current | |
Governed by the token's app scope. Acts onvolume Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/apps/{app_name}/volumes/{volume_id} | Get a single volume's details. | read | App token | Current | |
Governed by the token's app scope. Acts onvolume Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/volumes | Create a persistent volume in a region for an app's Machines to mount. | write | App token | Current | |
A volume is pinned to a region and a size in gigabytes. Acts onvolume Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /v1/apps/{app_name}/volumes/{volume_id}/extend | Extend a volume to a larger size. | write | App token | Current | |
Volumes can grow but not shrink. Acts onvolume Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/apps/{app_name}/volumes/{volume_id} | Delete a volume and the data stored on it. | write | App token | Current | |
Deletion permanently removes the stored data. Acts onvolume Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
SecretsList an app's secret names and set or remove a named secret that its Machines can read.3 | ||||||
| GET | /v1/apps/{app_name}/secrets | List an app's secret names. | read | App token | Current | |
Returns the names of an app's secrets, not their values. Acts onsecret Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/apps/{app_name}/secrets/{secret_name} | Create or update a named secret an app's Machines can read at runtime. | write | App token | Current | |
Secrets hold credentials passed to Machines as environment variables. Acts onsecret Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/apps/{app_name}/secrets/{secret_name} | Delete a named secret from an app. | write | App token | Current | |
Removes the secret; Machines stop receiving it on their next start. Acts onsecret Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
TokensRequest an OpenID Connect token a Machine can present to a third-party service.1 | ||||||
| POST | /v1/tokens/oidc | Request an OpenID Connect token a Machine can present to a third-party service. | write | App token | Current | |
The aud parameter sets the audience claim on the returned JWT. Acts ontoken Permission (capability) App tokenVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
The Machines API does not push events to a webhook URL. An app or AI agent that needs to know when a Machine reaches a state waits on it or polls instead.
| Event | What it signals | Triggered by |
|---|
Fly.io limits how fast an app or AI agent can call, through a per-second quota set separately for each action, with a short burst allowance on top.
Fly.io sets the rate limit per action rather than as one shared quota. Most actions, such as creating or starting a Machine, are limited to 1 request per second per action, with a short burst allowance up to 3 per second. Reading a Machine is higher, at 5 per second with a burst up to 10 per second. Deleting apps has its own ceiling of 100 per minute. Exceeding a limit returns an HTTP 429.
List endpoints, such as listing an app's Machines or volumes, return the full set as a JSON array rather than paging through a cursor. Filtering is done with query parameters on the request, for example listing apps for a given organization with org_slug.
Requests and responses are JSON. A Machine is defined by a config object that holds the image, environment, mounts, services, and guest size; the guest sets CPU kind, CPU count, and memory in megabytes. There is no single documented payload size limit across the API.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | Bad Request | The request was malformed or invalid, for example an unrecognized target state passed to the wait method or an invalid Machine update. The JSON body carries an error field with a message. | Read the error message in the body, correct the request, and resend. |
| 401 | Unauthorized | The API token is missing, invalid, or expired. | Send a valid token in the Authorization Bearer header. |
| 404 | Not Found | The app, Machine, volume, or lease does not exist, or the token's scope cannot reach it. A get-lease call returns 404 when no lease is held. | Confirm the path and that the token is scoped to the app holding the resource. |
| 408 | Request Timeout | A wait call timed out before the Machine reached the requested state within the timeout window. | Call wait again to keep waiting, or read the Machine to check its current state. |
| 409 | Conflict | The request conflicts with the current state, most often acquiring a lease on a Machine that is already leased. | Wait for or release the existing lease, then retry. |
| 429 | Too Many Requests | A per-action rate limit was exceeded, such as more than 1 create per second or more than 100 app deletions per minute. | Slow the calls for that action and retry after a short pause. |
The Machines API answers under a single path version, v1, and the underlying OpenAPI specification reports its own version as 1.0. New methods are added under v1 rather than minting a new path version.
The Machines API is served under a single path version, v1, at https://api.machines.dev/v1, generated from an OpenAPI specification that reports its own version as 1.0. New methods are added under v1 rather than as a new path version, so an integration on v1 keeps working and gains them. The public API for fast-booting virtual machines first shipped on 24 May 2022.
Fly.io added a first-party MCP server to flyctl, run with 'fly mcp server', letting an AI agent manage apps, Machines, volumes, and secrets through the Model Context Protocol. It defaults to a local stdio connection and can be exposed remotely. Announced on 19 May 2025.
Machine suspend, which saves a Machine's memory state so it resumes in a fraction of a second, was enabled in all Fly.io regions, alongside autosuspend. This added the suspend method to the Machines resource. Announced on 24 July 2024.
An integration calls the v1 path and gains new methods as Fly.io adds them, without a version bump.
Fly.io changelog ↗Bollard AI sits between a team's AI agents and Fly.io. Grant each agent exactly the access it needs, read or write, action by action, and every call is checked and logged.