Groundfloor Docs

Coderunners API

Create, upload, deploy, run and promote Coderunners.

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

Coderunner is the deploy path for functions, jobs, schedules, and services. No App is required. See Developers for gf deploy and MCP.

See also: Coderunner

Auth

All routes: Authorization: Bearer <JWT>. Forwarded to Coderunner with x-client-key (workspace id) and x-userid.

Mint a short-lived CR JWT for testing from curl or Postman:

POST /v1/workspaces/{workspace_id}/coderunner/dev-token

Permission: write. A read-only member, or a token that only carries read, gets 403. Every mint is recorded in the audit log.

Response: token, expires_in (seconds; 300 by default), base_url (the public Coderunner API URL to call with the token), workspace_id (send as the x-client-key header) and user_id (send as x-userid). Tokens can't be revoked, so treat one like any other bearer and let it expire.

List / create

MethodPathPermissionNotes
GET/v1/workspaces/{workspace_id}/coderunnersreadQuery: environment_id, app_id
POSTsamewriteBody below. Returns 201

Create body:

FieldRequiredDefaultNotes
titleYes1-120 characters (name is accepted as an alias)
runtimeNonodeA language slug from GET …/coderunners/languages
workload_typeNofunctionfunction | job | schedule | service
slugNoDerived from titleLowercase letters, digits and hyphens, up to 63 characters. A slug already used in the workspace is 409
environment_idNoThe active dev environment (or the only active one)
app_idNononeLinks the coderunner to an App; doesn't change how it deploys
scheduleNononeCron expression, for workload_type: schedule
descriptionNononeUp to 2,000 characters

Workspace site auth is not required.

GET /v1/workspaces/{workspace_id}/coderunners/languages

Lists the languages Coderunner supports; use the runtime slugs it returns. Permission: read

Get / update / delete / status

MethodPathPermission
GET…/coderunners/{id}read
PATCH…/coderunners/{id}write
DELETE…/coderunners/{id}delete
GET…/coderunners/{id}/statusread

GET / list / status flatten workload_type, schedule, last_job_name, and last_job_status from the manifest.

status also returns deployment_status, the state of the current deployment: 1 pending, 2 processing, 3 deployed, 4 failed, 5 deleting, 6 deleted, 7 stopped, or null when unknown. deployment_status_message gives the reason, usually when it failed. While the current deployment has failed, an app address or custom domain that routes to it answers 503 instead of an empty response.

Versions and upload

MethodPathPurposePermission
POST…/{id}/uploadMultipart ZIP → new version (max 64 MiB)write
GET…/{id}/versionsList versionsread
GET…/{id}/versions/{version_id}Get versionread
GET…/{id}/versions/{version_id}/downloadDownload packageread
DELETE…/{id}/versions/{version_id}Delete versionwrite
POST…/{id}/definitionEnsure upstream definitionwrite

Deploy and run

POST /v1/workspaces/{workspace_id}/coderunners/{id}/deploy

Body (optional): version_id, cpu, memory, envVariable, inject_secret_keys, minReplicas, maxReplicas (or replicas to set both at once — this is the autoscaling band for function/service; each is 0-100), schedule, suspended.

envVariable can't carry secrets: a key that looks like a credential is rejected with 422 and the offending keys are named in the response. That covers names ending in _PASSWORD, _SECRET, _PRIVATE_KEY, _API_KEY, _ACCESS_KEY or _SECRET_KEY, common database connection names (DATABASE_URL, POSTGRES_PASSWORD, MONGODB_URI and similar) and anything starting with MANAGED_DB_.

Don't pass secrets at deploy time: with the platform's default configuration, inject_secret_keys returns 422. Have the workload read secrets at runtime with an M2M client that has the secrets:read scope. See Secrets in a workload.

POST /v1/workspaces/{workspace_id}/coderunners/{id}/run

Body (optional): the event JSON object itself, forwarded as-is to the handler — do not wrap it in payload. A non-object body is 422. Targeting is query-only: ?version_id=, ?deployment_id=, ?deployment_url= (preferred).

curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"hello":"world"}' \
  "https://platform.groundfloor.cloud/v1/workspaces/$WORKSPACE_ID/coderunners/$CODERUNNER_ID/run"

Groundfloor calls your Coderunner with its own short-lived token; your token is never passed on.

MethodPathPermission
GET…/{id}/deployment (optional ?version_id= adds version_deployment_id, the deployment recorded for that version)read
GET…/{id}/deployments/{deployment_id}read
DELETE…/{id}/deployments/{deployment_id}write
GET…/{id}/logs/{job_id}read

Permission for deploy/run: write

Public Deployment URL on list/detail comes from the upstream ExternalEndpoint for non-schedule workloads.

Public front door

Service workloads are served from a stable public host. You can take one off the public front door, and put it back, without redeploying:

MethodPathPermission
GET…/coderunners/{id}/publicread
PUT…/coderunners/{id}/publicwrite

PUT body: { "enabled": false }. Both return { "enabled": true, "host": "…" }. With enabled: false the service's branded host and any custom domains stop resolving; the host is kept, so enabled: true restores the same URL. Turning a service off still works on a frozen workspace; turning it back on does not (423). Only service workloads can be switched; other workloads return 400.

Git source

MethodPathPermission
GET / PUT / PATCH / DELETE…/{id}/git-sourceread / write
POST…/{id}/git-source/deploywrite (202)

PUT creates the git source, or replaces it if one exists. Body:

FieldRequiredNotes
repo_urlYeshttps URLs only
refNoDefaults to main
subdirNoSubdirectory to build from
secret_keyNoName of a workspace secret holding a token for private repos

The create response includes webhook_secret once; PATCH accepts the same fields (all optional) plus rotate_webhook_secret: true, which mints a new secret and returns it in that response. Webhook: POST /v1/webhooks/git/{source_id}, verified with a GitHub X-Hub-Signature-256 signature. POST …/git-source/deploy returns 409 while a git deploy is already running, and 404 if no git source is configured.

Domains

Custom domains belong to Apps, not Coderunners. The …/{id}/domains routes here always return 400 ("Custom domains are available on Apps, not standalone coderunners."). To give a service a domain, make it the primary_coderunner_id of a standalone App and use the App's domain routes. A Coderunner on its own is reached at its Deployment URL.

Compare and promote

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

Promote body:

FieldRequiredNotes
source_environment_idYesMust differ from the target; both environments must be active
target_environment_idYes
pairs[]YesAt least one. Each pair: source_coderunner_id (required), optional target_coderunner_id (created in the target environment if omitted) and version_id
dry_runNoDefault false. A dry run is synchronous and starts no work

Response: {source_environment_id, target_environment_id, dry_run, promoted[], failed[], background}. Each result has source_coderunner_id, target_coderunner_id, version_id, deployment_id, created_target, error and a status of accepted (the promote continues in the background; background is true), completed or failed. Poll compare or the target's deployment status to see the outcome.

Limits

LimitValue
ZIP upload (…/upload)64 MiB; larger is 413, an empty ZIP is 400
replicas, minReplicas, maxReplicas0-100
Duplicate slug on create409
Credential-shaped envVariable keys422 (see Deploy and run)

Runners and executions

MethodPathPurposePermission
GET/v1/workspaces/{workspace_id}/runnersList deploymentsread
GET…/runners/{runner_id}Get runnerread
POST…/runners/{runner_id}/restartRestartwrite
GET/v1/workspaces/{workspace_id}/executionsList executionsread