A job is not a replacement for exec or managed sessions — it’s a layer above them that also owns sandbox lifecycle for the run. Use exec or a session when you already have a sandbox and want to run something on it directly; use a job when you want the run tracked as its own resource, optionally on a sandbox created and torn down just for it.
Creating a job
POST /v1/job either blocks for the result or starts durably and returns immediately, controlled by the wait body field (default false).
bonya job CLI command — jobs are reached through REST or an SDK. MCP: job_create covers both modes — pass wait: true to block for the result or leave it false to get a run_id back immediately.
The request body (JobRequest) requires exactly one of existing_sandbox_id/new_sandbox, and exactly one of cmd/script:
string
Run on a sandbox that already exists. Mutually exclusive with
new_sandbox.object
Create a sandbox for this run.
{ template_id?, version?, name? } — template_id may be omitted to use the deployment’s configured default template, the same as sandbox create.string[]
Argv to run. Mutually exclusive with
script.object
{ body_b64, interpreter?, args?, filename? } — a script the caller wrote or uploaded, run instead of cmd. The body travels inline and is size-capped.object
Optional script (same shape as
script) executed after the sandbox is ready and before the job’s own command or script. A non-zero exit stops the job before it runs.object
Extra environment variables for the job.
string
Working directory for the job.
string
default:"delete"
delete or keep — what happens to a sandbox this run created once the run ends, however it ends (including cancellation). Not meaningful for existing_sandbox_id.boolean
default:"false"
Applies to
existing_sandbox_id only. false makes a suspended target a failure rather than an implicit resume, since resuming is a billable action the caller may not have intended.boolean
default:"false"
false starts the run durably and returns immediately with a run_id (202). true blocks for the result, bounded by the caller’s own request timeout (200).Idempotency-Key header — see Idempotency.
Run lifecycle
A run’sstatus moves through the states the job’s own command or script produces, ending in a terminal state that carries a result (exit_code, captured output, and similar exec-style fields). Whether you passed wait: true or not, the same result shape appears once the run completes — wait only controls whether the create call itself blocks for it.
Listing and inspecting runs
GET /v1/job is paginated, newest first; optionally filtered to sandbox_id or schedule_id (runs fired by a job schedule). GET /v1/job/{run_id} returns the full run detail: the run summary plus the stored job spec that started it and an activity timeline. This is the endpoint to call before an edit-and-rerun, described above.
bonya job CLI command. MCP: job_list pages through runs; job_get returns one run’s full detail.
Canceling a run
POST /v1/job/{run_id}/cancel requests cancellation rather than forcibly terminating — the run’s own cleanup (for example, deleting a sandbox it created, per disposition) still executes before the run reaches its terminal state.
bonya job CLI command. MCP: job_cancel requests cancellation the same way.
Job schedules
A job schedule wraps a job spec with timing, so it runs on its own without a caller invoking create each time.bonya job-schedule CLI command. MCP: job_schedule_create covers this.
string[]
Cron expressions to fire on. Exactly one of
cron_expressions/interval_s/run_at_unix_nanos is required.integer
Fire every N seconds.
integer
One-shot: a single future calendar time. Refused if it names a time in the past — a one-shot schedule is this field set, not a separate mechanism.
string
IANA zone (e.g.
US/Pacific) that cron_expressions is evaluated against. Empty is UTC.integer
Random delay added to each fire, in seconds.
string
default:"skip"
What a fire does when the previous run from the same schedule is still going:
skip (drop the new fire), buffer_one, or allow_all.boolean
default:"false"
Create the schedule already paused.
object
required
The job spec to run on each fire — same shape and same-field requirements as job create, above.
PATCH /v1/job-schedule/{schedule_id} replaces the whole schedule — timing and job spec together — so re-send every field you want to keep, not just the one you’re changing.
GET /v1/job-schedule/{schedule_id} returns the schedule’s timing, stored job spec, pause state, next run time, and its recent run ids: the last 10 fires (oldest first, including manual triggers), each a valid GET /v1/job/{run_id} id — enough to follow a schedule straight to its recent runs without a separate list call.
POST /v1/job-schedule/{schedule_id}/pause sets the paused state without deleting the schedule. POST /v1/job-schedule/{schedule_id}/trigger fires one run immediately, ignoring the schedule’s own timing — useful for testing a schedule without waiting for it. DELETE /v1/job-schedule/{schedule_id} permanently removes it, without canceling any run currently in flight.
bonya job-schedule CLI command. MCP: job_schedule_list and job_schedule_trigger cover the calls above (plus job_schedule_get, _update, _pause, and _delete for the rest of the surface).