Skip to main content

App Environment Job Runs API

The Job Runs API lets customers run finite workloads in Quave ONE. A Job is a first-class workload type, separate from long-running Apps, Databases & Services, and Functions. Each run creates a visible JobRun with status, logs, timing, and exit-code metadata.

Make sure to read the Get Started document to understand how the API works.

Note: These endpoints accept user tokens. Environment tokens are also accepted when the token is scoped to the target app environment.

Create a Job app

Create a Job app with the Apps API by setting dockerPreset to JOB. Job apps do not require port, hosts, HTTP probes, or a long-running deployment. They do require a runnable image or source/build configuration before a run can start.

Example:

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name": "Rails migrations",
"accountId": "ACCOUNT_ID",
"dockerPreset": "JOB",
"useImage": true,
"image": "ghcr.io/acme/my-rails-app:2026-06-19",
"jobConfig": {
"command": "bin/rails db:migrate",
"shell": true,
"timeoutSeconds": 1800,
"ttlSecondsAfterFinished": 21600,
"backoffLimit": 0
},
"isCliDeployment": true
}' \
https://api.quave.cloud/api/public/v1/app

Then create an app environment for that Job app with the App Environments API. You can set or override jobConfig at the environment level.

Job Config object

jobConfig can be stored on the Job app or overridden on the Job environment. Safe execution fields can also be provided for one run, and those per-run values are snapshotted into the JobRun so history stays stable even if the app config changes later.

FieldTypeDescription
commandStringCommand to execute. Required before a run starts.
argsArray of stringsOptional argument list. If shell is true, you can also include arguments directly in command.
shellBooleanRuns command through /bin/sh -lc when true. Default: true.
workingDirStringOptional working directory inside the container.
timeoutSecondsIntegerActive deadline for the run. Default: 1800.
ttlSecondsAfterFinishedIntegerKubernetes TTL after a run finishes. Default: 21600; minimum: 300.
backoffLimitIntegerKubernetes Job retry backoff limit. Default: 0.
maxConcurrencyIntegerMaximum active runs for this Job environment. Default: 1 unless concurrent runs are allowed.
allowConcurrentRunsBooleanAllows unlimited active runs when true and maxConcurrency is omitted. Default: false.
scheduleStringReserved for scheduled Jobs.
scheduleTimeZoneStringReserved for scheduled Jobs.

Per-run overrides only accept command, args, shell, workingDir, timeoutSeconds, ttlSecondsAfterFinished, and backoffLimit. Configure concurrency and schedule fields on the Job app or environment, not in a run request.

To edit saved environment-level Job settings after creation, use:

PATCH /api/public/v1/app-env/job-config

This saves the Job environment override as a pending deploy change. Default future runs continue to use the previously applied Job config until you apply the change. It does not mutate historical JobRun snapshots. See the App Environments API.

Config hierarchy and Apply Changes

Default JobRuns use the applied deployment snapshot for the environment. The snapshot is captured from the app and environment settings when a Job image/build is deployed or when pending Job settings are applied.

The saved settings are layered into the applied snapshot:

  1. App-level jobConfig defaults.
  2. Environment-level jobConfig overrides.
  3. Applied deployment snapshot for the current content.
  4. Per-run jobConfig override from the run request.

When an applied deployment snapshot exists, a default run uses that snapshot. If no applied snapshot exists yet, Quave ONE falls back to the current app and environment settings. Editing app-level or environment-level jobConfig creates pending deploy changes and does not change default JobRuns until you:

  • call POST /api/public/v1/app-env/apply-changes,
  • call the MCP tool apply-app-env-changes,
  • click Apply changes in the dashboard, or
  • pass applyImmediately: true to an update endpoint that supports it.

If you want to test a new command without applying it as the saved default, pass a per-run jobConfig in the POST /api/public/v1/app-env/job-runs request. That override is stored only on the new JobRun.

Run a Job now

Send a POST request to /api/public/v1/app-env/job-runs.

You must provide either appEnvId or envName. You can optionally provide contentId to run a specific image/build version and any jobConfig override for this run.

If you omit jobConfig, the run uses the currently applied Job config snapshot, or the current saved app and environment settings if no applied snapshot exists yet. If you include jobConfig, those values override the applied snapshot for this run only.

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"appEnvId": "APP_ENV_ID",
"jobConfig": {
"command": "bin/rails db:migrate"
}
}' \
https://api.quave.cloud/api/public/v1/app-env/job-runs

Example response:

{
"success": true,
"jobRun": {
"jobRunId": "JOB_RUN_ID",
"accountId": "ACCOUNT_ID",
"appId": "APP_ID",
"appEnvId": "APP_ENV_ID",
"contentId": "CONTENT_ID",
"triggerType": "API",
"command": "bin/rails db:migrate",
"args": [],
"shell": true,
"statusLabel": "Queued",
"isInProgress": true,
"isTerminal": false,
"isSuccess": false,
"isFailed": false,
"status": "QUEUED",
"region": "us-5",
"timeoutSeconds": 1800,
"ttlSecondsAfterFinished": 21600,
"backoffLimit": 0,
"createdAt": "2026-06-19T18:00:00.000Z"
}
}

List Job runs

Send a GET request to /api/public/v1/app-env/job-runs.

Query parameterTypeDescription
appEnvIdStringApp environment ID. Required unless envName is provided.
envNameStringCLI environment name. Required unless appEnvId is provided.
limitIntegerMax runs to return. Default: 50. Maximum: 100.
curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/app-env/job-runs?appEnvId=APP_ENV_ID&limit=20'

Get one Job run

Send a GET request to /api/public/v1/app-env/job-run.

Query parameterTypeDescription
jobRunIdStringJob run ID.
appEnvIdStringOptional app environment ID. Required when authenticating with an environment token.
envNameStringOptional CLI environment name. Can be used instead of appEnvId for environment-token context.
curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/app-env/job-run?jobRunId=JOB_RUN_ID'

Cancel a Job run

Send a POST request to /api/public/v1/app-env/job-run/cancel.

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"jobRunId": "JOB_RUN_ID",
"appEnvId": "APP_ENV_ID"
}' \
https://api.quave.cloud/api/public/v1/app-env/job-run/cancel

Cancel deletes the Kubernetes Job for that run when it already started, then marks the JobRun as CANCELED.

When authenticating with an environment token, include appEnvId or envName in the request body so Quave ONE can verify the token belongs to the run's environment.

Rerun a Job

Send a POST request to /api/public/v1/app-env/job-run/rerun.

By default, rerun uses the previous run's immutable config snapshot and content. You can override contentId or jobConfig for the new run.

When authenticating with an environment token, include appEnvId or envName in the request body so Quave ONE can verify the token belongs to the previous run's environment.

curl -X POST \
-H 'Authorization: YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"jobRunId": "PREVIOUS_JOB_RUN_ID",
"jobConfig": {
"timeoutSeconds": 3600
}
}' \
https://api.quave.cloud/api/public/v1/app-env/job-run/rerun

Get Job run logs

Send a GET request to /api/public/v1/app-env/job-run/logs.

Query parameterTypeDescription
jobRunIdStringJob run ID.
appEnvIdStringOptional app environment ID. Required when authenticating with an environment token.
envNameStringOptional CLI environment name. Can be used instead of appEnvId for environment-token context.
streamStringOptional stream filter: INFO or ERROR.
searchStringOptional search text.
sizeIntegerNumber of log entries.
fromStringStart timestamp in ISO 8601 format.
toStringEnd timestamp in ISO 8601 format.
isLiveBooleanWhether to retrieve recent live logs.
scrollIdStringScroll ID for pagination.
callScrollBooleanContinue an existing scroll.
newScrollBooleanStart a new scroll.
curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
'https://api.quave.cloud/api/public/v1/app-env/job-run/logs?jobRunId=JOB_RUN_ID&stream=INFO'

Job statuses

StatusDescription
QUEUEDRun was recorded and the internal executor is queued.
STARTINGQuave ONE is creating the Kubernetes Job.
RUNNINGKubernetes reports an active Job pod.
SUCCEEDEDJob completed successfully.
FAILEDJob failed or exited with a non-zero status.
CANCELEDRun was canceled by the user or the Kubernetes Job was deleted.
TIMED_OUTActive deadline or Quave ONE polling deadline was exceeded.
NOT_FOUNDKubernetes Job was not found; it may have been deleted or garbage-collected before Quave ONE persisted the terminal state.

JobRun responses also include helper fields for release automation:

FieldDescription
statusLabelHuman-readable label for the status.
isInProgressTrue for QUEUED, STARTING, and RUNNING.
isTerminalTrue when no more status changes are expected.
isSuccessTrue only for SUCCEEDED.
isFailedTrue for terminal statuses that should block a deploy, including FAILED, CANCELED, TIMED_OUT, and NOT_FOUND.

Rails migration pattern

For Rails migrations, create a Job app using the same image as the app version you want to migrate, then set the Rails command that exists inside your container, usually bin/rails db:migrate for apps with Rails binstubs or bundle exec rails db:migrate:

{
"command": "bin/rails db:migrate",
"shell": true,
"timeoutSeconds": 1800,
"backoffLimit": 0
}

This keeps the migration visible to the customer as a normal JobRun with status, logs, billing, and audit history instead of hiding it as an internal release step.

Recommended flow:

  1. Create the Job app with dockerPreset: "JOB".
  2. Create the Job environment.
  3. Deploy or build the image for that environment.
  4. Apply pending Job setting changes if the command was edited after deployment.
  5. Create a JobRun.
  6. Poll the run until it reaches a terminal status.
  7. Fetch the JobRun logs.

Poll by JobRun ID before app deploy

The create response includes jobRun.jobRunId. Use it to poll the run:

JOB_RUN_ID="JOB_RUN_ID_FROM_CREATE_RESPONSE"

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
"https://api.quave.cloud/api/public/v1/app-env/job-run?jobRunId=${JOB_RUN_ID}"

Only deploy the web app when the response has status: "SUCCEEDED" or isSuccess: true. Stop the release and fetch logs when isFailed is true:

curl -X GET \
-H 'Authorization: YOUR_TOKEN' \
"https://api.quave.cloud/api/public/v1/app-env/job-run/logs?jobRunId=${JOB_RUN_ID}&stream=INFO"

For CLI-based pipelines, the same deploy gate is:

IMAGE="ghcr.io/acme/rails-app:${GITHUB_SHA}"

quaveone job run \
--env acme-rails-migration-prod \
--image "$IMAGE" \
--command "bin/rails db:migrate" \
--wait \
--timeout-seconds 1800 \
--backoff-limit 0 \
--logs-on-failure

quaveone deploy \
--env acme-rails-web-prod \
--image "$IMAGE" \
--wait

For the full customer guide, see Jobs.