Skip to main content

Deploying a Project

POST /projects/deploy

Submit a ProjectDescriptor to create or update a project. The endpoint is idempotent: deploying the same descriptor twice produces all noop actions and returns immediately with the unchanged project status.

curl -X POST https://api.foundrydb.com/projects/deploy \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"descriptor": {
"name": "my-todo-app",
"resources": [
{
"logical_name": "db",
"kind": "postgres",
"spec": { "plan": "tier-2", "storage_gb": 50 }
},
{
"logical_name": "web",
"kind": "app",
"spec": {
"image": "ghcr.io/myorg/todo:latest",
"port": 3000
}
}
],
"dependencies": {
"web": ["db"]
}
}
}'

The response is 202 Accepted with the initial deploy state and the computed plan:

{
"project_id": "a1b2c3d4-...",
"deployment_id": "e5f6a7b8-...",
"status": "Pending",
"plan": [
{ "logical_name": "db", "kind": "postgres", "action": "create" },
{ "logical_name": "web", "kind": "app", "action": "create" }
]
}

Provisioning is asynchronous. Use project_id and deployment_id to poll for the final outcome.

Plan actions

The engine computes one action per resource before touching anything:

ActionWhen
createResource does not yet exist
updateResource exists and its spec changed (app image or env only in Phase 1)
noopResource is unchanged and Running
unmanagedResource was previously provisioned but is absent from the new descriptor

The plan array in the response reflects these decisions before any resource is created. You can inspect it to confirm what the engine will do.

Dependency ordering and concurrency

The engine topologically sorts the resource graph and provisions resources in order: a resource is started only after all of its declared (and inferred) dependencies reach Running. Resources with no mutual dependency provision concurrently.

For the example above, the engine provisions db first. Once db is Running it provisions web, at which point it also wires the database attachment and injects DATABASE_URL into the web container.

What gets injected

The engine injects connection credentials into the app container automatically when it wires attachments. You do not need to set these variables yourself.

From a postgres attachment

When exactly one database is attached:

VariableValue
DATABASE_URLFull connection string including TLS parameters

When multiple databases are attached each gets a set of prefixed variables. The prefix is derived from the attached service's name.

From a files attachment

When exactly one files service is attached:

VariableValue
S3_ENDPOINTHTTPS endpoint of the object storage service
S3_BUCKETBucket name
S3_ACCESS_KEYAttachment-scoped access key ID
S3_SECRETAttachment-scoped secret access key

When multiple files services are attached each gets a prefixed set: {NAME}_S3_ENDPOINT, {NAME}_S3_BUCKET, {NAME}_S3_ACCESS_KEY_ID, {NAME}_S3_SECRET_ACCESS_KEY.

Always injected

VariableValue
FOUNDRY_API_TOKENA scoped platform token with services:read and files:read permissions
FOUNDRY_API_URLhttps://api.foundrydb.com

The FOUNDRY_API_TOKEN is minted once at app creation and preserved across re-deploys. Re-deploying with a changed image or env never re-mints or drops the token.

Polling for completion

Poll GET /projects/{name} until status is Running:

curl https://api.foundrydb.com/projects/my-todo-app \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

The response carries the project's aggregate status and a resources array with per-resource status and status_detail.

To inspect the computed plan and track a specific deploy attempt, poll the deployment endpoint:

curl https://api.foundrydb.com/projects/my-todo-app/deployments/$DEPLOYMENT_ID \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

This returns the deployment record (including plan, status, and error), the live project_status, and a per-resource status snapshot.

Project lifecycle

Pending -> Provisioning -> Wiring -> Running

RollingBack -> Failed (first-deploy failure or abort; no orphans)
Failed (re-deploy failure; healthy resources retained)
Deleting -> Deleted (explicit teardown)
StatusMeaning
PendingDeploy accepted; queued
ProvisioningOne or more resources are being created or updated
WiringAll resources are running; cross-resource credentials are being injected
RunningAll resources are provisioned and wired
RollingBackA terminal first-deploy failure triggered atomic teardown
FailedTerminal after rollback, or after a re-deploy failure (healthy resources retained)
DeletingExplicit teardown in progress
DeletedTerminal after a clean teardown

Re-deploy semantics

A project can only be re-deployed when its status is terminal (Running, Failed, or Deleted). Submitting a deploy while one is already in progress or while the project is being torn down returns 409 Conflict.

On a re-deploy the engine diffs the new descriptor against current state:

  • Changed app spec (image or env): a PatchAppService is applied in place.
  • Changed non-app spec: the change is recorded; in-place mutation of a running database or files service is not performed in Phase 1.
  • Unchanged resource: no action.
  • Resource removed from descriptor: flagged unmanaged, never touched.

A re-deploy failure never tears down previously-provisioned healthy resources.

Teardown

Explicit teardown deletes every provisioned resource in the project and revokes the project's scoped platform token. Resources that were never provisioned are removed immediately from the project record.

curl -X DELETE https://api.foundrydb.com/projects/my-todo-app \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

Returns 202 Accepted. Poll GET /projects/my-todo-app until status is Deleted. This operation is irreversible for any provisioned resource.

Listing and inspecting projects

# List all projects
curl https://api.foundrydb.com/projects \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

# Get a specific project with per-resource status
curl https://api.foundrydb.com/projects/my-todo-app \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

Next steps