Skip to content
runlot
ReferenceAPIDeploy

Deploy

Covers 7 API operations in the deployments category.

MethodPathDescription
GET/v1/orgs/{orgSlug}/projects/{projectName}/deploymentsDeployment history
POST/v1/orgs/{orgSlug}/projects/{projectName}/deploymentsDeploy after uploading the artifact
POST/v1/orgs/{orgSlug}/projects/{projectName}/deployments/uploadsOpen an incremental deploy upload: list the bundle's files, learn which are missing
POST/v1/orgs/{orgSlug}/projects/{projectName}/deployments/uploads/{uploadId}/blobsUpload a batch of file contents for an open upload session
POST/v1/orgs/{orgSlug}/projects/{projectName}/deployments/uploads/{uploadId}/commitAssemble the uploaded files into the bundle and deploy it
GET/v1/orgs/{orgSlug}/projects/{projectName}/deployments/{deploymentId}One deployment, with the status of any migrations the CP applied
POST/v1/orgs/{orgSlug}/projects/{projectName}/rollbackRolls back to a previous version

GET /v1/orgs/{orgSlug}/projects/{projectName}/deployments

The newest version comes first. Deployments are immutable and are never deleted.

operationId listDeployments

Status codeDescriptionResponse body
200List of deploymentsobject
403——
404——

POST /v1/orgs/{orgSlug}/projects/{projectName}/deployments

The body is the bundle tarball itself. The bundle describes itself: its runlot.json manifest lives inside, so one digest covers code, assets and config together.

Bundle layout (gzipped tar):

runlot.json manifest (required) worker/ worker code. can be omitted for a static site assets/ static assets. can be omitted

Uploading the same digest again creates a new version. A deployment records the event "serve this now", so each upload stays a separate entry in the rollback history.

When the upload succeeds, the new deployment goes live immediately.

Provenance headers (optional). Because the body is the bundle itself, metadata travels as headers. runlot deploy reads the git metadata of the folder it deploys and sends it; with no git repository it sends nothing. These values are display-only and never affect authorization or placement.

X-Runlot-Git-Commit HEAD hash (hex, 7-64 chars) X-Runlot-Git-Message first line of the commit message, percent-encoded (truncated at 200 chars) X-Runlot-Git-Branch branch name, percent-encoded X-Runlot-Git-Dirty true|false — whether there were uncommitted changes

With ?preview=<name> the bundle is deployed to a preview and production keeps serving what it was already serving. Version numbers are the preview's own. The 201 body is the usual Deployment with one extra field, preview, holding the Preview it went to — including the preview's hostname.

operationId createDeployment

ParameterLocationRequiredTypeDescription
previewqueryNoSlugDeploy to the named preview instead of production. Slug rules, at most 30 characters — it becomes half of the DNS label <preview>--<project>. Creates the preview if it does not exist. Exceeding the plan's per-project preview count answers 409 preview_limit.
dbqueryNostringWhat the preview's env.db points at. Ignored without preview. - branch — its own database, copied from the parent project's newest generation. The default, and a full copy of the parent's data at the moment it was taken. - shared — the preview runs against production's database. A destructive statement in the preview is destructive in production. - none — no env.db at all.
fromqueryNostringCopy the branch from this generation instead of the newest one — any key listed for the parent project. Only meaningful with db=branch. An incomplete backup chain answers 409 chain_broken, the same as a restore.
X-Runlot-Git-CommitheaderNostring—
X-Runlot-Git-MessageheaderNostring—
X-Runlot-Git-BranchheaderNostring—
X-Runlot-Git-DirtyheaderNoboolean—

Request body: application/gzip · string

Status codeDescriptionResponse body
201The created deploymentDeployment
400——
403——
404——
409over_project_limit — the organization holds more active projects than its plan allows (usually after a downgrade). Every project's deploys are paused until one is disabled or deleted, or the plan is upgraded; nothing is deleted automatically. project_disabled — this project is disabled; enable it first.Error
413Bundle exceeds the limitError

POST /v1/orgs/{orgSlug}/projects/{projectName}/deployments/uploads

The incremental form of createDeployment (docs/incremental-deploy-upload.md §1). The body lists every entry the bundle tar would hold, in tar order — the server does not re-sort it, so what it assembles at commit is what the client would have tarred. A directory is { "path": "assets/", "type": "dir" }.

The list is checked against the bundle limits (entry count, path rules, per-entry and total size, the single runlot.json) and the org/licence/preview checks of createDeployment run here, so a deploy that is going to be refused is refused before any byte moves.

The answer names the sha256 hashes the server does not already hold for this project (a preview uses its parent's). Upload those with uploadDeploymentBlobs, then commitDeploymentUpload. A session lives for expiresAt (one hour).

An older control plane answers 404 here; a client falls back to the single createDeployment POST.

operationId createDeploymentUpload

ParameterLocationRequiredTypeDescription
previewqueryNoSlugAs on createDeployment.
dbqueryNostringAs on createDeployment. Ignored without preview.
fromqueryNostringAs on createDeployment.

Request body: application/json · object

Status codeDescriptionResponse body
201The session, and the hashes still to uploadDeploymentUpload
400——
403——
404——
409over_project_limit, project_disabled, preview_limit — as on createDeployment.Error
413The listed bundle exceeds a limitError

POST /v1/orgs/{orgSlug}/projects/{projectName}/deployments/uploads/{uploadId}/blobs

The body is a gzip tar of regular entries, each named by the lowercase sha256 hex of its content. Clients keep each body at most 2 MiB of input; a single file larger than that goes alone. Re-sending a blob is harmless, so a failed batch is retried as a whole.

400 blob_mismatch — an entry's content does not hash to its name. 400 blob_not_listed — a hash the session's file list did not mention. 404 upload_not_found — no such session, or it expired.

operationId uploadDeploymentBlobs

Request body: application/gzip · string

Status codeDescriptionResponse body
200What this batch stored, and what the session still lacksobject
400——
403——
404——
413The batch or one of its entries exceeds a limitError

POST /v1/orgs/{orgSlug}/projects/{projectName}/deployments/uploads/{uploadId}/commit

Assembles the gzip tar in the listed order and deploys it exactly as createDeployment would; the 201 body is the same (Deployment, plus preview for a preview session). Takes the same provenance headers.

Idempotent per session: committing a session that already produced a deployment returns that deployment instead of making a new version, so a client retries a commit that timed out at the edge.

409 blobs_missing — a listed blob is not held (it may have been swept between list and commit); details.missing names them. Upload those and commit again. 404 upload_not_found — no such session, or it expired.

operationId commitDeploymentUpload

ParameterLocationRequiredTypeDescription
X-Runlot-Git-CommitheaderNostring—
X-Runlot-Git-MessageheaderNostring—
X-Runlot-Git-BranchheaderNostring—
X-Runlot-Git-DirtyheaderNoboolean—
Status codeDescriptionResponse body
201The created deploymentDeployment
400——
403——
404——
409blobs_missing, or any 409 of createDeployment.Error

GET /v1/orgs/{orgSlug}/projects/{projectName}/deployments/{deploymentId}

The same object the history lists, plus migrations — which only a deployment the control plane migrates for you carries.

Normally migrations/ is applied by the client: runlot deploy connects over the wire and runs the files, watched by the person who typed the command. A preview deploy is the exception, because the CLI can only name the parent project's database and running a preview's migrations there is the accident db=branch exists to prevent. For those the control plane applies them to the branch instead, after the branch finishes pouring, and this is where the outcome is read.

A preview whose id was returned by createDeployment is readable under its parent project's name — the name the caller deployed through.

operationId getDeployment

Status codeDescriptionResponse body
200The deploymentDeployment & object
403——
404——

POST /v1/orgs/{orgSlug}/projects/{projectName}/rollback

Points the project at an older deployment without creating a new one. Bundles are immutable, so a rollback serves exactly the same bytes that were being served at that version.

Refused with 409 the same way a deploy is: over_project_limit when the organization holds more active projects than its plan allows, project_disabled when this project is disabled.

operationId rollback

Request body: application/json · object

Status codeDescriptionResponse body
200Rolled-back deploymentDeployment
403——
404——

On this page