Skip to content

Projects API

A project is a deployable unit of code. Its slug is either static (served from Firebase/CDN) or container (run on Kubernetes); when you create with auto, PandaStack detects which one your repo is and resolves the slug for you. Secret values are never returned by the project endpoints below — the env array carries only plaintext variables. Secret names come from a dedicated endpoint, and a value requires an explicit per-secret reveal.

For the conceptual guides see Projects overview, Static sites, Container apps, Environment variables & secrets, Custom domains, and Redirects, rewrites & headers.

All routes below are org-scoped and require authentication. The role floor is noted per endpoint.

ProjectDetail (returned by get / create-result-adjacent reads):

FieldTypeNotes
idnumber
namestring | nullSubdomain-safe name.
slugstatic | container | string | nullDeployment target.
branchstring | nullTracked branch.
repositoryNamestring | nullowner/repo.
organizationIdstringBIGINT serialized as string.
projectUrlstring | nullLive URL.
enabledbooleanWhether the project is active (see toggle).
isPublicboolean
languagestring | nulle.g. nodejs, python, go, docker.
runtimestring | null
regionstring | null
baseDirstring | nullBuild root directory.
buildDirstring | nullStatic output directory.
buildCmdstring | null
startCmdstring | nullContainer start command.
healthCheckPathstring | null
dockerFilePathstring | null
tierobject | nullInstance tier.
disknumber | nullPersistent disk (GB).
autoScalingobject | null{ enabled, minReplicas, maxReplicas, targetCpuPercent }.
enableAnalyticsboolean
autoDeploybooleanDeploy on push.
envEnvVar[]{ name, value }; secrets excluded.
totalDeploymentsnumber
successDeploymentsnumber
failedDeploymentsnumber
latestDeploymentobject | null{ status, commitId, createdAt }.
createdAt / updatedAtstring | null

ProjectListItem (returned in lists) is a compact subset: id, name, slug, branch, repositoryName, organizationId, projectUrl, enabled, latestDeployment, updatedAt.


GET /v1/projects

Cursor-paginated list of projects in the active org. Secret values are never returned.

Auth: any member.

Query params (ProjectListQuery):

ParamTypeRequiredNotes
cursorstringnoOpaque cursor from a prior nextCursor.
limitintno1–100, default 50.
slugstringnoFilter by static or container.
Terminal window
curl "https://api.pandastack.io/v1/projects?limit=20&slug=container" \
-H "Authorization: Bearer $PANDASTACK_TOKEN"
{
"success": true,
"data": {
"items": [
{
"id": 812,
"name": "marketing-site",
"slug": "static",
"branch": "main",
"repositoryName": "acme/marketing",
"organizationId": "1907",
"projectUrl": "https://marketing-site.pandastack.app",
"enabled": true,
"latestDeployment": { "status": "succeeded", "commitId": "a1b2c3d", "createdAt": "2026-07-14T10:00:00Z" },
"updatedAt": "2026-07-14T10:00:12Z"
}
],
"nextCursor": null
}
}

POST /v1/projects

Creates a project and triggers its first deploy. Static projects deploy fully; container projects need DNS and deploy asynchronously. A source scan runs at build time. The request body is a discriminated union on slug — pick static, container, or auto.

Auth: member.

FieldTypeRequiredNotes
slug"static"yesDiscriminator.
namestringyesRFC-1123 label: lowercase letters, numbers, hyphens; 1–63 chars.
repositoryNamestringyesowner/repo.
branchstringnoDefault main.
baseDirstringnoDefault ./.
buildDirstringnoDefault dist.
buildCmdstringnoDefault npm run build.
autoDeploybooleannoDefault false.
prPreviewbooleannoDefault false.
isPublicbooleannoDefault true.
enableAnalyticsbooleannoDefault false.
envEnvVar[]noDefault [].
secretsEnvVar[]noDefault []; stored base64. Values are never returned by this endpoint.

slug: "container"CreateContainerInput

Section titled “slug: "container" — CreateContainerInput”
FieldTypeRequiredNotes
slug"container"yesDiscriminator.
namestringyesRFC-1123 label.
repositoryNamestringyesowner/repo.
branchstringnoDefault main.
regionstringnoDefault us.
languageenumyesauto | nodejs | python | go | docker.
isPublicbooleannoDefault true.
baseDirstringnoDefault ./.
buildCmdstringnoDefault "".
startCmdstringnoDefault "".
entryPointstringnoDefault "".
healthCheckPathstringnoDefault /.
dockerFilePathstringnoDefault ./Dockerfile.
dockerBuildContextDirstringnoDefault ./.
tier{ key: string }noInstance tier.
disknumberno0–100 GB, default 0.
autoScalingobjectno{ enabled, minReplicas 1–50, maxReplicas 1–100, targetCpuPercent 1–100 }.
autoDeploybooleannoDefault false.
prPreviewbooleannoDefault false.
enableAnalyticsbooleannoDefault false.
env / secretsEnvVar[]noDefault [].

Let PandaStack detect static vs container from the repo. Same base fields as static/container but most build fields are optional overrides:

FieldTypeRequiredNotes
slug"auto"yesDiscriminator.
namestringyesRFC-1123 label.
repositoryNamestringyesowner/repo.
branchstringnoDefault main.
baseDirstringnoDefault ./.
isPublicbooleannoDefault true.
autoDeploy / prPreview / enableAnalyticsbooleannoDefault false.
env / secretsEnvVar[]noDefault [].
buildCmd, buildDir, startCmdstringnoOptional overrides.
tier{ key: string }no
disknumberno0–100.
autoScalingobjectnoSee above.

Response (CreateProjectResult):

FieldTypeNotes
projectIdnumber
namestring
deploymentIdnumberFirst deploy row.
deploymentUuidstringUse to poll status / stream logs.
Terminal window
curl -X POST https://api.pandastack.io/v1/projects \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"slug": "auto",
"name": "my-app",
"repositoryName": "acme/my-app",
"branch": "main",
"autoDeploy": true,
"env": [{ "name": "NODE_ENV", "value": "production" }]
}'
{
"success": true,
"data": { "projectId": 900, "name": "my-app", "deploymentId": 5501, "deploymentUuid": "d-8f3c2a1e" }
}

See Auto-detect deploys and Build & buildpacks.

POST /v1/projects/static-upload

Create or re-upload a static project directly from a ZIP archive (no Git repo). The upload is content-abuse scanned, then stored in GCS.

Auth: member. Content-Type: multipart/form-data.

FieldTypeRequiredNotes
staticFilesfile (ZIP)yesMax 30 MB.
namestringyesRFC-1123 label.

Response: { projectId, name, deploymentId, uploadId, liveUrl, fileCount }.

Terminal window
curl -X POST https://api.pandastack.io/v1/projects/static-upload \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-F "name=my-static" \
-F "staticFiles=@site.zip"

GET /v1/projects/check-name

Auth: any member. Query: ?name=<candidate>. Response (NameAvailability): { name, available }.

Terminal window
curl "https://api.pandastack.io/v1/projects/check-name?name=my-app" \
-H "Authorization: Bearer $PANDASTACK_TOKEN"

GET /v1/projects/:name

Auth: any member. Returns the full ProjectDetail (see the project object).

Terminal window
curl https://api.pandastack.io/v1/projects/my-app \
-H "Authorization: Bearer $PANDASTACK_TOKEN"

DELETE /v1/projects/:name

Deletes the project and tears down its infrastructure. Static teardown is synchronous; container teardown is asynchronous via the worker.

Auth: admin. Response: { deleted: true, slug, async }.

Terminal window
curl -X DELETE https://api.pandastack.io/v1/projects/my-app \
-H "Authorization: Bearer $PANDASTACK_TOKEN"

PATCH /v1/projects/:name/name

Auth: member. Body (RenameProjectInput):

FieldTypeRequiredNotes
namestringyesRFC-1123 label, 1–63 chars, ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$.

Returns the updated ProjectDetail.

PATCH /v1/projects/:name

Auth: member. Body (UpdateProjectInput) — all fields optional:

FieldTypeNotes
buildCmdstringMax 2000.
startCmdstringMax 2000.
baseDirstringMax 500.
buildDirstringMax 500.
healthCheckPathstringMax 500.
dockerFilePathstringMax 500.
disknumber0–1000.
tier{ key: string }Instance tier.
autoScalingobject{ enabled, minReplicas 1–50, maxReplicas 1–100, targetCpuPercent 1–100 }.
enableAnalyticsboolean
autoDeployboolean
envEnvVar[]Plaintext vars.
secretsSecretUpsert[]Full replacement set. Omit an entry’s value to keep the stored one.

Returns the updated ProjectDetail. When the save triggers an automatic redeploy (an env/secret/compute change on a container app), the response also carries triggeredDeploymentUuid so you can follow that build’s logs.

secrets replacement semantics. The array you send is the new secret set: a stored name you omit is deleted. An entry may leave out value, which means “keep whatever is stored under this name”. That is what lets a client hold only the values the user actually typed or revealed and still save safely — sending [{"name":"API_KEY"}] alongside a new secret preserves API_KEY rather than blanking it.

See Autoscaling & scale-to-zero and Environment variables & secrets.

GET /v1/projects/:name/secrets

Auth: member. Returns [{ "name": string }] — names only, never values. Use it to show which secrets exist without exposing them.

POST /v1/projects/:name/secrets/reveal

Auth: member. Body: { "name": string }. Returns { "name": string, "value": string } for that one secret, with Cache-Control: no-store.

POST rather than GET so the value never lands in a URL, an access log, or an intermediary cache. One secret per call — there is no bulk-dump endpoint. Every call is written to the project’s audit log with the secret’s name and the user who read it. Returns 404 if no secret by that name exists.

POST /v1/projects/:name/toggle

Auth: member. Body (ToggleProjectInput): { "enable": boolean }. Returns ProjectDetail.

POST /v1/projects/:name/rollback

Roll back to a previous deployment.

Auth: member. Body (RollbackInput): { "deploymentUuid": string }. Response: { deploymentUuid }.

Terminal window
curl -X POST https://api.pandastack.io/v1/projects/my-app/rollback \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "deploymentUuid": "d-previous-uuid" }'

POST /v1/projects/:name/deploy

Triggers a deploy (create or redeploy), optionally at a specific commit. Writes a deployment row and an outbox event, then returns 202 (async).

Auth: member. Body:

FieldTypeRequiredNotes
operationstringnoDeploy operation hint.
commitShastringnoDeploy a specific commit.

Response: { deploymentId, deploymentUuid, projectId }.

Terminal window
curl -X POST https://api.pandastack.io/v1/projects/my-app/deploy \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "commitSha": "a1b2c3d4" }'

POST /v1/projects/:name/redeploy

Redeploys the current source; 202 async. Auth: member. Body: none. Response: { deploymentId, deploymentUuid, projectId }.

GET /v1/projects/:name/deployments

Cursor-paginated list of a project’s deployments.

Auth: any member. Query (DeploymentListQuery): cursor (optional), limit (1–100, default 20).

Deployment object:

FieldTypeNotes
idnumber
deploymentUuidstring | null
statusqueued | running | succeeded | failed | string
commitIdstring | null
commitShastring | null
branchstring | null
enableboolean | null
actorobject{ userId, name, username }.
createdAt / updatedAtstring | null

GET /v1/deployments/:uuid/status

Read-once status of a deployment (the UI polls this).

Auth: any member. Response (DeploymentStatusInfo):

FieldTypeNotes
deploymentUuidstring
statusqueued | running | succeeded | failed | string
terminalbooleantrue once the deploy has finished (succeeded or failed).
commitIdstring | null
branchstring | null
createdAt / updatedAtstring | null
projectNamestring | null
repositoryNamestring | null
projectUrlstring | null
Terminal window
curl https://api.pandastack.io/v1/deployments/d-8f3c2a1e/status \
-H "Authorization: Bearer $PANDASTACK_TOKEN"

GET /v1/deployments/:uuid/logs/stream

Server-Sent Events stream of build logs. It replays history from Elasticsearch, tails live output, de-duplicates lines, and closes when the deploy reaches a terminal state (or after a 30-minute cap).

Auth: any member. Because this is SSE, pass the token (and org, for JWTs) as query params: ?access_token=<token>&organization_id=<orgId>. See SSE authentication.

Events emitted: log, system, status, done. Each log line matches the LogLine shape: { message, timestamp, stream } where stream is stdout | stderr | system.

Terminal window
curl -N "https://api.pandastack.io/v1/deployments/d-8f3c2a1e/logs/stream?access_token=$PANDASTACK_TOKEN&organization_id=1907"
event: log
data: {"message":"Installing dependencies…","timestamp":"2026-07-14T10:00:03Z","stream":"stdout"}
event: status
data: {"status":"running"}
event: done
data: {"status":"succeeded"}

Attach your own hostnames to a project. Domains are provisioned through Cloudflare custom hostnames (SSL-for-SaaS). Concept guide: Custom domains.

GET /v1/projects/:name/domains

Auth: any member. Response: CustomDomain[].

POST /v1/projects/:name/domains

Auth: member. Body: AddCustomDomainInput. Response: CustomDomain (returns DNS verification details to set at your registrar).

POST /v1/domains/:domainId/verify

Re-checks a domain’s verification/SSL status.

Auth: any member. Response: CustomDomain.

DELETE /v1/domains/:domainId

Auth: member. Response: { deleted: true }.


Configure gateway routing rules per project. On containers these are synced to Kong. Concept guide: Redirects, rewrites & headers.

GET /v1/projects/:name/headers (any member) — returns HeaderRule[].

PUT /v1/projects/:name/headers (member) — body SetHeadersInput; persisted to Postgres and Kong-synced for containers. Returns HeaderRule[].

GET /v1/projects/:name/redirects (any member) — returns RedirectRule[].

PUT /v1/projects/:name/redirects (member) — body SetRedirectsInput; persisted to Postgres and Kong-synced. Returns RedirectRule[].

Terminal window
curl -X PUT https://api.pandastack.io/v1/projects/my-app/redirects \
-H "Authorization: Bearer $PANDASTACK_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "redirects": [{ "source": "/old", "destination": "/new", "status": 301 }] }'