cancel_job
Cancel a job that is STILL QUEUED. Free, repeatable, never refunds.
ALWAYS branch on `cancelled`; reason + next_action say what to do. A queued job
was never charged (the worker charges), so credits_refunded is always 0. A RUNNING
job cannot be stopped: it finishes, CHARGES and persists. Cancelled reads as
status "failed" with error.error.details.cancelled true; to re-run, resubmit with
a FRESH idempotency_key (reusing the cancelled one replays the cancelled result).
Errors: unauthorized, forbidden (key lacks the spend scope), not_found, rate_limited.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | The job to cancel. Account-scoped: an id you do not own reads as not_found, exactly like get_job. | |
| api_key | No | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> unauthorized. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| error | No | Not returned by this tool. Named here because get_job(job_id) afterwards carries error.error.details.cancelled true (code 'conflict', details.reason 'cancelled'): THAT is how you tell your own cancel apart from a genuine job failure. | |
| job_id | No | The job the cancel targeted. | |
| reason | No | Why the call ended the way it did, in one sentence. | |
| status | No | The job's status AFTER the attempt: 'failed' when cancelled, else whatever it really is (running / succeeded / failed). | |
| cancelled | No | true only when this call moved a still-QUEUED job to terminal. false means nothing changed; `reason` says why and `next_action` says what to do. | |
| next_action | No | The exact next call to make, if any. | |
| credits_refunded | No | Always 0. A queued job was never charged (the charge runs inside the worker), and a running job cannot be stopped, so a cancel never refunds. | |
| already_cancelled | No | true when an earlier cancel_job had already cancelled this job, so this call was a no-op rather than a miss. |