Deploy your application by creating builds through the API.
/organizations/:orgId/projects/:projectId/endpointsReturns a flat list of the live URLs for a project: the default URL plus any connected (active) custom domains.
{
"endpoints": [
"https://abc123.justdeploy.site",
"https://www.example.com"
]
}/organizations/:orgId/projects/:projectId/buildsReturns the most recent builds first. Use limit (1-100, default 50) and cursor for pagination: pass the nextCursor from the previous response, and stop when it comes back null.
{
"builds": [
{
"id": "b1234567-abcd-...",
"title": "Add password reset email",
"description": "Adds the reset flow and the SES template.",
"commit": null,
"version": null,
"runtime": "nodejs22.x",
"size": 524288,
"status": "ready",
"error": null,
"suggestion": null,
"isLive": true,
"canRedeploy": false,
"createdAt": "2026-03-20T...",
"updatedAt": "2026-03-20T..."
}
],
"nextCursor": null,
"redeployAvailability": { "status": "available" }
}isLive marks the build the project is actually serving. Usually that is simply the newest one, so it looks redundant until it is not: when a deploy fails its health check the platform rolls back, and the newest build is then not the one running. Read this rather than assuming the top of the list.
Only trust it on a finished build. The image is swapped before the health check runs, so a build still deploying is reported live while it may yet be rolled back. Ignore isLive on anything whose status is not ready or error.
false also covers "could not tell". If the platform cannot read what is attached it marks the whole page false rather than failing the request, so no live build in the list is not proof that nothing is serving.
canRedeploy marks a build that the redeploy endpoint below will accept: the ten most recent successful builds other than the one deployed. A successful build can be ineligible for a reason nothing else on the row shows, so read this flag rather than testing status yourself.
redeployAvailability separates the two ways a page can come back with no candidates. "available" means the list is the real answer, even if nothing on it can be rolled back; "temporarily_unavailable" means the platform could not read what is deployed, in which case isLive and canRedeploy are false everywhere and the honest move is to retry after retryAfter seconds.
/organizations/:orgId/projects/:projectId/buildsCreate a new build. Returns a presigned upload URL for uploading your application code as a .zip file.
The body is optional, and so is every field in it. Send title and description to say what this build changes: they are what a person sees in the console build list, which otherwise shows only a status and a timestamp. Write them like a commit message, title being the one-line summary (up to 128 characters) and description the details (up to 4096). Either one over its limit is a 400; both are trimmed, and an empty string is stored as null.
{
"title": "Add password reset email",
"description": "Adds the reset flow and the SES template. Needed before launch."
}{
"build": {
"id": "b2345678-efgh-...",
"title": "Add password reset email",
"description": "Adds the reset flow and the SES template. Needed before launch.",
"commit": null,
"version": null,
"runtime": null,
"size": null,
"status": "pending",
"error": null,
"suggestion": null,
"createdAt": "2026-03-20T...",
"updatedAt": "2026-03-20T..."
},
"url": "https://upload.justdeploy.net/..." // Presigned upload URL
}Upload your zip file to the presigned URL:
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/zip" \
--data-binary @app.zipIf a build for this project is already in progress, the request returns 409 Conflict with the existing build so you can poll it and retry once it finishes.
{
"status": "Conflict",
"message": "A build is already in progress for this project.",
"build": {
"id": "b1234567-abcd-...",
"status": "building",
"createdAt": "2026-05-05T..."
}
}/organizations/:orgId/projects/:projectId/builds/:buildIdCheck the status of a build. Poll this endpoint until the status reaches ready or error.
{
"build": {
"id": "b2345678-efgh-...",
"title": "Add password reset email",
"description": "Adds the reset flow and the SES template.",
"commit": null,
"version": null,
"runtime": "nodejs22.x",
"size": 524288,
"status": "ready",
"error": null,
"suggestion": "Consider committing a lock file so future builds stay reproducible.",
"warning": null,
"canceledAt": null,
"isLive": true,
"createdAt": "2026-03-20T...",
"updatedAt": "2026-03-20T..."
}
}On success, suggestion may carry advisory improvement notes (a missing lock file, a dev-only start command, hardcoded URLs, secrets committed in source) and is usually null. It is not an error and never blocks the deploy: read it, then apply the suggestions on your next code change rather than building again for them.
warning is different: it means this deploy may not actually work, and it can appear even when the status is ready, because the health check accepts any response rather than requiring a working page, so an app with no routes still finishes green. Read it before telling anyone the deploy succeeded.
Stop polling when canceledAt is set, whatever the status says. See below.
isLive is only worked out once the build is finished, so it stays false for the whole polling window. Do not read it as "the deploy did not land" before the status settles.
/organizations/:orgId/projects/:projectId/rebuildBuild the source the project is already running, with the current environment variables, and deploy the result. Nothing to upload, no body, and no separate deploy step. Requires a deploy-scope key.
This is the path for a changed environment variable. Variables are baked into the image while building, so editing one leaves the running app on the old value until something builds again. Re-zipping unchanged source just to carry a new value is wasted work.
There is no build to choose: it always rebuilds whatever is deployed right now. To go back to older source, use the redeploy endpoint below instead.
The new build inherits the title and description of the build it copies, here and on redeploy below, because it is made from the same source.
{
"build": {
"id": "b3456789-ijkl-...",
"status": "pending",
"title": "Add password reset email",
"description": "Adds the reset flow and the SES template.",
...
}
}Poll the new build exactly as you would after POST /builds. Refusals are specific: 409 while another build is running, or when the project has never deployed successfully, or when the deployed build did not finish successfully; 410 when the original source archive is gone; 503 when the platform could not determine what is deployed, which is worth retrying.
/organizations/:orgId/projects/:projectId/builds/:buildId/redeployDeploy an earlier version again, from the source that build was made from. Pass the id of a build whose canRedeploy is true. No body. Requires a deploy-scope key.
Three neighbouring things have three names. Rebuild rebuilds the source already deployed, with the current environment variables. Redeploy, here, rebuilds an older source you choose. The automatic rollback is neither: when a deploy fails its health check the platform puts the previous image back by itself. Only the last of the three actually reverses anything, which is why this endpoint is not called rollback.
This is not an undo and nothing is reattached. It copies that build’s original archive into a new build and runs the whole pipeline again, so it costs a normal build in time and money, and it can fail where it once succeeded.
Two things stay in the present, deliberately. The environment variables used are the ones read when the new analysis starts, not the ones that were set when the old build ran; and the app’s runtime credential from back then is not restored, so a key that has since been revoked stays revoked. Check GET /environments first if the old source depends on a variable that has changed.
{
"build": {
"id": "b4567890-mnop-...",
"status": "pending",
"title": "Add password reset email",
"description": "Adds the reset flow and the SES template.",
...
}
}Poll the new build as you would after POST /builds. Refusals: 404 when the build is not in this project; 409 while another build is running, when the target did not finish successfully or was cancelled, when it is already the deployed build, or when it has fallen outside the ten most recent eligible builds; 410 when its original archive is gone, in which case pick another build; 503 when the platform could not determine what is deployed, which is worth retrying.
/organizations/:orgId/projects/:projectId/builds/:buildId/cancelStop a build that is still on its way. Requires a deploy-scope key.
This marks the build rather than killing it. Whatever stage is running finishes (an analysis call runs to completion, and a container build that already started still produces an image) and the build is then dropped at the next stage boundary. Stopping during analyzing therefore saves the most, because the build stage never starts at all.
The project stops being locked the moment the build is marked, so a new build can be created straight away without waiting for the stopped one to wind down.
{
"build": {
"id": "b2345678-efgh-...",
"status": "building",
"canceledAt": "2026-03-20T...",
...
}
}status is not overwritten. It stays where the build stopped, so you can still tell how far it got: analyzing means nothing was built, building means an image was produced, and error means it was going to fail anyway. Treat any build with a canceledAt as finished.
Cancelling an already-cancelled build returns 200 again. A 409 means it can no longer be stopped: either it has finished, or it has reached deploying, where the image swap and health check are already running and cutting in would leave a broken image serving.
# 1. Create a build
BUILD=$(curl -s -X POST \
"https://api.justdeploy.net/organizations/ORG_ID/projects/PROJECT_ID/builds" \
-H "Authorization: Bearer $ACCESS_KEY:$SECRET_KEY")
UPLOAD_URL=$(echo $BUILD | jq -r '.url')
BUILD_ID=$(echo $BUILD | jq -r '.build.id')
# 2. Upload your code
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/zip" \
--data-binary @app.zip
# 3. Poll for completion
curl -s \
"https://api.justdeploy.net/organizations/ORG_ID/projects/PROJECT_ID/builds/$BUILD_ID" \
-H "Authorization: Bearer $ACCESS_KEY:$SECRET_KEY"JustDeploy detects your runtime automatically from your project. The runtime field returned in the build response is one of:
nodejs24.xnodejs22.xpython3.14python3.13python3.12java21java17