Groundfloor Docs

Apps API

Register Shell and standalone apps, publish releases, and promote across environments.

Base path: /v1/workspaces/{workspace_id}/apps

Apps are optional product packaging. Do not create an app solely to deploy a workload — use the Coderunners API. app_kind=coderunner is rejected on create.

See also: Apps

List apps

GET /v1/workspaces/{workspace_id}/apps?kind=product
QueryValues
kindproduct (default: shell_federated and standalone), shell_federated, standalone, all. The legacy value coderunner is also accepted; anything else returns 422
include_archivedtrue to include archived apps. Default false

Returns at most 200 apps.

Permission: read

Create app

POST /v1/workspaces/{workspace_id}/apps

Permission: write (workspace site auth is not required)

Body fieldRequiredNotes
nameYesDisplay name
slugYesUnique in workspace. Lowercase letters, digits and hyphens, 1 to 63 characters; a duplicate returns 409
app_kindNoshell_federated (default) or standalone
environment_idNoTarget environment. Defaults to the dev environment (or the only active one)
primary_coderunner_idStandalonePrimary service coderunner. Must be in this workspace (404 otherwise), not archived (409), and a service workload (400)
manifestNoJSON manifest
git_repo_urlNoSource repo metadata

Get / update / delete

MethodPathPermission
GET/v1/workspaces/{workspace_id}/apps/{app_id}read
PATCHsame — name, manifest, git_repo_url, primary_coderunner_idwrite
DELETEsame — archives the app. Returns 204delete
POST…/apps/{app_id}/restore — un-archives the app. 409 if the app is not archiveddelete
GET…/apps/by-slug?slug= — Shell config for a shell_federated app by slug (404 if not found)read

App access

GET /v1/workspaces/{workspace_id}/apps/{app_id}/access
PUT /v1/workspaces/{workspace_id}/apps/{app_id}/access

Controls who can launch the app in the Shell. The PUT replaces the whole setting:

{
  "visibility": "restricted",
  "grants": [{ "role": "editor", "action": "read" }]
}
  • visibility is workspace (default; anyone who can read the workspace can launch the app) or restricted (only the site roles listed in grants).
  • Each grant names a workspace site role. action is always read.
  • The built-in admin role is a role like any other. On a restricted app, list admin in grants if admins should launch it; if you leave it out, admins lose access.

Permission: ddl on the workspace. Site users get 403, whatever their role, admin included; manage app access from the Client Portal or with a builder token.

Releases (Shell federated)

MethodPathPurposePermission
POST…/apps/{app_id}/releases/upload-urlInit upload (bundle: remote_entry | zip)write
PUT…/releases/{release_id}/contentUpload bytes (≤32 MiB). Returns 204write
POST…/releases/{release_id}/finalizePublishwrite
GET…/releasesListread
GET…/releases/currentCurrent published (404 if none)read

Releases apply to shell_federated apps only (400 for other kinds, 410 for an archived app).

The upload-url body takes bundle (default remote_entry), an optional label (up to 120 characters) and a required size_bytes: the exact size you will upload, greater than 0 and at most 32 MiB (33,554,432 bytes). A missing size_bytes returns 422 and an oversized one returns 413. The size is checked again on finalize.

For a zip bundle, the archive is extracted on finalize. The extracted contents can't exceed 256 MiB or 2,000 entries; an archive over either limit is refused with 400. A zip can be finalized once, so a refused archive needs a new upload. Finalize returns 409 if no remoteEntry.js is found in the upload.

Custom domains

Custom domains belong to Apps, not Coderunners: a standalone App's domains point at its primary_coderunner_id. These routes work for apps of kind standalone (and legacy coderunner apps); any other app kind returns 400, an archived app 410:

MethodPathPermission
GET/v1/workspaces/{workspace_id}/apps/{app_id}/coderunner/domainsread
POST…/apps/{app_id}/coderunner/domainswrite (201)
DELETE…/apps/{app_id}/coderunner/domains/{domain}write (204)
POST…/apps/{app_id}/coderunner/domains/{domain}/verifywrite
GET / PUT…/apps/{app_id}/coderunner/publicread / write

POST body: { "domain": "example.com" } (a bare hostname, up to 253 characters). 409 when the domain is already added or collides with a branded host.

Each domain item has domain, status (pending, verifying, verified, active or error), cname_target (create a CNAME from your domain to this host; apex domains need ALIAS/ANAME), verification_record ({type, name, value}: a DNS TXT record that proves you own the domain), last_checked_at and detail (the result of the last check). verify re-checks DNS and returns {domain, status, verified, detail}. The …/public routes behave like the Coderunner ones.

CLI: gf domains ls|add|rm|verify --app <id|slug>.

Compare and promote

GET /v1/workspaces/{workspace_id}/apps/compare?source_environment_id=&target_environment_id=
POST /v1/workspaces/{workspace_id}/apps/promote

Promote body: source_environment_id, target_environment_id, pairs[] (source_app_id, optional target_app_id / source_release_id), optional dry_run.

Permission: compare read, promote write

Shell config

GET /v1/workspaces/{workspace_id}/apps/{app_id}/shell-config

Authenticated bootstrap metadata for Shell hosts. Permission: read

Public by-slug bootstrap lives under /v1/public/… — see API authentication and Shell API reference.