---
title: "Cancel a running detached job"
method: DELETE
path: "/api/v1/jobs/{id}"
---

# Cancel a running detached job

`DELETE /api/v1/jobs/{id}`

## Request

- Base URL: `https://aicommander.dev`
- URL: `https://aicommander.dev/api/v1/jobs/{id}`
- Auth: one of:
  - none
  - HTTP bearer

Stops the job, terminating its WHOLE process tree (a training run is rarely a single process) and releasing any GPU it had reserved. Cancelling a job that has already finished is not an error — you simply get its final state back. Cancellation is not reversible: work since the last checkpoint is lost, so confirm with the user before cancelling something long-running. For a job that is still alive the reply is not immediate: the endpoint waits a few seconds for the process tree to actually go, so that it reports what became of the job instead of its state before the signal was sent. Allow a client timeout of at least 10 seconds. If the job outlives that wait the reply says so explicitly — the cancellation was still accepted, so do not repeat it.

## Path parameters

- `id` string, required

## Query parameters

- `code` string, required

## Response `200`

The job's state after the call.

- object
  - `ok` true, required
  - `job` JobSummary, required: A detached job's state as seen by a caller.
    - `jobId` string, required: 16 hex characters, generated by the machine (never caller-supplied).
    - `name` string, required: The label you passed, or one the machine generated.
    - `status` 'running' | 'exited' | 'unknown', required: `running` = the process was alive when the machine looked. `exited` = the exit code is authoritative. `unknown` = the process is gone and no exit code was recorded — an agent restart, a `SIGKILL`, the OOM killer, an escalated cancellation, or any cancellation on Windows (`taskkill /F`) all produce it, since no exit marker is written; the outcome is genuinely unknowable and must never be reported as success.
    - `exitCode` integer, nullable, required: Non-null only for `exited`.
    - `startedAt` integer, required: Epoch milliseconds.
    - `endedAt` integer, nullable, required: Epoch milliseconds the job ended, or null while it is still running. NOT null for `unknown` jobs: one that recorded no exit still gets a timestamp, but an INFERRED one — see endedAtApproximate, and check that flag before presenting this value as the moment the job finished.
    - `endedAtApproximate` boolean: Present and true when `endedAt` is an ESTIMATE, not an observed instant. A job that recorded no exit marker (SIGKILL, the OOM killer, an escalated cancel, any Windows cancel, the agent going down) has no true end time, so the machine infers one from the last write to its output log — up to ~2 minutes late on Windows, where NTFS defers last-write-time updates while the log is held open. ABSENT means the timestamp was observed and is exact; absent is also what an agent older than 1.0.50 sends for every job. Never quote an approximate value as the finish time, and never measure a duration from one without saying it is approximate.
    - `unknownReason` 'cancelled' | 'killed_by_signal' | 'job_script_removed' | 'vanished_no_output_windows' | 'vanished_no_output_posix' | 'vanished_with_output_windows' | 'vanished_with_output_posix' | 'undetermined': Present ONLY on `unknown` jobs, and only when the machine recorded one: WHAT THE MACHINE OBSERVED about the ending, captured at the moment it settled the job and never re-derived afterwards. It does NOT soften the status — `unknown` still means no exit code was recorded and success may never be assumed. Every token names evidence, not a cause. `cancelled` = someone asked for this job to stop. `killed_by_signal` = the machine watched the job's shell end and it ended on a signal, the only one of these backed by an observed death. `job_script_removed` = the per-job script files were gone from the job's directory (on Windows, the signature of security software quarantining them mid-run — see https://aicommander.dev/antivirus). `vanished_no_output_*` / `vanished_with_output_*` = nothing watched the ending; the scripts were intact and the log was empty, or the log had output in it. A `vanished_*` job may have been killed OR may have completed normally after bypassing the exit marker (a command that `exec`s away or overrides the wrapper's exit trap) — the evidence does not say which. `undetermined` = the machine could not read the job's own directory. ABSENT is normal and means the machine said nothing: an agent older than 1.2.2, or a token this relay does not recognise (which is dropped rather than passed on). Treat an absent value as no information, never as an error, and do not assume this list is closed — a newer agent may add a token, which arrives as absence until the relay learns it.
    - `gpuIndex` integer, nullable, required: The card reserved for this job, or null if none was requested.
    - `logBytes` integer, required: Current size of the job's output log, so you can page it without a probe read.
    - `truncated` boolean, required: True once the log hit its 256 MiB cap and the agent stopped appending. The job itself was NOT killed.
    - `command` string: The job's command line — present ONLY when the request asked for it with include_command=true.
    - `operationId` string: The `operation_id` the job was started with, if any.
    - `locks` string[]: The named locks the job took, if any.
    - `deduplicated` boolean: Present and true on a start answered with the job an earlier start under the same `operation_id` created: nothing new was started.

## Other responses

- `400`: Missing/invalid fields. On the job endpoints this is also how the MACHINE reports a request it can never run (`reason: "invalid_request"` — an empty command, a relative `cwd`, a `gpu_index` that names no card): the request itself is at fault, so correct the field the message names rather than retrying it.
- `401`: A token was supplied but is not a valid API key/admin/OAuth token.
- `403`: Resolved but refused. Causes include expired anonymous access; Free/anonymous file transfer; `reason: plan_device_limit`; independent operator block/approval; or `error: reactivation_required`. For plan_device_limit, delete an older saved machine to promote the next or upgrade to Pro. For file transfer, use Pro. Do not conflate either with operator approval.
- `404`: Session code or alias not found (anonymous callers get a unified message that does not confirm existence).
- `429`: Per-IP or global tool-call rate limit exceeded.
- `503`: The session exists but the agent is offline or disconnected mid-command.

---

[API](https://skmtc.dev/aicommander/apis/ai-commander-remote-shell-and-long-running-jobs-api.md) · [All operations](https://skmtc.dev/aicommander/apis/ai-commander-remote-shell-and-long-running-jobs-api/llms.txt) · [OpenAPI document](https://skmtc.dev/aicommander/apis/ai-commander-remote-shell-and-long-running-jobs-api/revisions/a6bb89f779a1?raw)
