> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/endpoints/job-cancel
> Every page is listed at https://docs.reversecontact.com/llms.txt, every endpoint with its credit cost and status at https://docs.reversecontact.com/catalog.json

# Cancel a job

> Stop a job an API key or an AI assistant started, for good. Items not started are never charged, and the results of the items done stay downloadable.

- Endpoint: `POST https://api.reversecontact.com/v2/jobs/:jobId/cancel`
- Credits: Free. Items that had not started are never charged.
- Answer: synchronous.
- Available in: REST API, MCP tool `cancel_job`, TypeScript and Python SDKs, CLI command `rc bulk cancel`.
- Last verified: 2026-11-04

## Introduction

The Cancel a job endpoint stops a job for good. Items already in flight may finish and are charged as usual; items not started are never charged and read `skipped` in the results. The job then reads `cancelled`, and the results of the items done can be downloaded with [Download job results](https://docs.reversecontact.com/endpoints/job-results). An item in flight finishes within the hour: until then, its row of the results file reads `running`, and [Read job results by page](https://docs.reversecontact.com/endpoints/job-rows) waits for it.

Any key of the workspace that can read a job can cancel it, whichever key started it: a rotated key can still stop a job its predecessor created. This covers every job of the [history](https://docs.reversecontact.com/endpoints/jobs) that an API key or an AI assistant started (`source` is `api` or `mcp`), a List import included. A job a person started in the app or with Ask is stopped there: cancelling it here returns `403 JOB_STARTED_BY_A_PERSON`.

Cancelling is **free** (0 credits), also at a zero balance, and final: a cancelled job is never resumed, and a new run needs a new job. Cancelling a job already cancelled answers it again. When the job has a `webhookUrl`, that URL receives the `cancelled` state.

## Authorization

| Name     | Type   | Required | Description                                                                                         |
| -------- | ------ | -------- | --------------------------------------------------------------------------------------------------- |
| `apikey` | string | Yes      | Your API key from the developer dashboard. Pass it via `Authorization: Bearer YOUR_API_KEY` header. |

## Request

The route is `POST /v2/jobs/:jobId/cancel`.

### Path parameters

| Name    | Type   | Required | Description                |
| ------- | ------ | -------- | -------------------------- |
| `jobId` | string | Yes      | The `id` of the job (UUID) |

The request has no body.

## Response structure

| Field     | Type    | Description                     |
| --------- | ------- | ------------------------------- |
| `success` | boolean | `true` if the job was cancelled |
| `error`   | `null`  | Always `null` on success        |

`data` is the cancelled job, with the fields of [Get a job](https://docs.reversecontact.com/endpoints/job#response-structure) and `status` set to `cancelled`. Its `progress` can still move while the items in flight finish, and `credits.used` stays `null` until the job has settled its credits (a List import counts them as it goes): read the job again to see the final counts.

`quotas` and `metadata` are the blocks of every V2 response: see [Usage](https://docs.reversecontact.com/endpoints/usage#response-structure).

## Errors

| Status | Code                      | Meaning                                                             |
| ------ | ------------------------- | ------------------------------------------------------------------- |
| `403`  | `JOB_STARTED_BY_A_PERSON` | A person started this job in the app or with Ask: stop it there     |
| `404`  | `JOB_NOT_FOUND`           | No job with this id in your workspace, or one your key may not read |
| `409`  | `JOB_NOT_CANCELLABLE`     | The job already completed or failed: there is nothing left to stop  |
| `422`  | `VALIDATION_ERROR`        | `jobId` is not a UUID                                               |

## Code examples

**Code Examples**

```bash [cURL]
curl -X POST https://api.reversecontact.com/v2/jobs/7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60/cancel \
  -H "Authorization: Bearer YOUR_API_KEY"
```
```javascript [JavaScript]
const jobId = "7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60";
const response = await fetch(`https://api.reversecontact.com/v2/jobs/${jobId}/cancel`, {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_API_KEY" }
});

const { data, error } = await response.json();
console.log(error ? error.code : `${data.status}, ${data.progress.remaining} items not started`);
```
```python [Python]
import requests

job_id = "7b1e4c2a-5d3f-4e8b-9a6c-1f2d3e4b5a60"
response = requests.post(
    f"https://api.reversecontact.com/v2/jobs/{job_id}/cancel",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)

payload = response.json()
print(payload["error"]["code"] if payload["error"] else payload["data"]["status"])
```

## Example response

**Example Response**

```json [200 - Cancelled]
{
  "success": true,
  "data": {
    "id": "5e8d2c1b-9a4f-4b3e-8c7d-6f5a4b3c2d10",
    "type": "enrich_persons",
    "entity": "persons",
    "status": "cancelled",
    "source": "api",
    "name": "Leads, Q4",
    "listId": null,
    "progress": {
      "total": 500,
      "processed": 120,
      "succeeded": 97,
      "failed": 23,
      "skipped": 0,
      "remaining": 380
    },
    "credits": { "estimated": 1000, "used": null },
    "estimatedSecondsRemaining": null,
    "error": null,
    "createdAt": "2026-11-04T10:02:00.000Z",
    "startedAt": "2026-11-04T10:02:01.000Z",
    "completedAt": "2026-11-04T10:04:30.000Z",
    "links": {
      "self": "/v2/jobs/5e8d2c1b-9a4f-4b3e-8c7d-6f5a4b3c2d10",
      "results": "/v2/jobs/5e8d2c1b-9a4f-4b3e-8c7d-6f5a4b3c2d10/results",
      "operation": null
    }
  },
  "error": null,
  "quotas": {
    "creditsConsumed": 0,
    "workspace": {
      "id": "ws_abc123",
      "credits": { "total": 10000, "used": 5472, "left": 4528 }
    }
  },
  "metadata": {
    "requestId": "2373b182-0821-45fb-b10f-37428d3b8087",
    "executionTimeMs": 48
  }
}
```
```json [409 - Already ended]
{
  "success": false,
  "data": null,
  "error": {
    "code": "JOB_NOT_CANCELLABLE",
    "message": "The job is completed and can no longer be cancelled"
  },
  "quotas": {
    "creditsConsumed": 0,
    "workspace": {
      "id": "ws_abc123",
      "credits": { "total": 10000, "used": 4528, "left": 5472 }
    }
  },
  "metadata": {
    "requestId": "d5f5c787-9f5f-42c4-9de8-f94144955cec",
    "executionTimeMs": 21
  }
}
```
