> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/endpoints/list-operation-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 List operation

> Cancel a List import for free and keep members already added. Lines in progress finish as usual; lines not started are never charged.

- Endpoint: `POST https://api.reversecontact.com/v2/lists/:listId/operations/:operationId/cancel`
- Credits: Free. Lines that had not started are never charged.
- Answer: synchronous.
- Available in: REST API, MCP tool `cancel_list_operation`, TypeScript and Python SDKs.
- Last verified: 2026-11-04

## Introduction

The Cancel a List operation endpoint stops an [import](https://docs.reversecontact.com/endpoints/list-intake-signals) for good. Lines already in flight finish and are charged as usual; lines that had not started are never charged and read `not_reached` in the [per-line results](https://docs.reversecontact.com/endpoints/list-operation#response-structure). Members already added stay on the List.

Any key of the workspace with the `lists` permission can cancel an import, whichever key started it: a rotated key can still stop an import its predecessor queued.

Cancelling is **free** (0 credits) and final: sending the import again with the same `operationId` returns the cancelled operation, and a new run needs a new `operationId`. Cancelling an operation already cancelled answers it again, and an operation already stopped by a List limit or a lack of credits is answered with its own `stopReason` (`list_member_limit`, `list_pending_intake_limit` or `insufficient_credits`). When the import 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/lists/:listId/operations/:operationId/cancel`.

### Path parameters

| Name          | Type   | Required | Description                                     |
| ------------- | ------ | -------- | ----------------------------------------------- |
| `listId`      | string | Yes      | List id (UUID)                                  |
| `operationId` | string | Yes      | The `operationId` of the import acknowledgement |

The request has no body.

## Response structure

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

`data` is the cancelled operation, with the fields of [Read a List operation](https://docs.reversecontact.com/endpoints/list-operation#response-structure): `status` is `cancelled`, `clientAction` is `done` and `stopReason` is `cancelled`, or the reason of an earlier stop. Its `progress` and `creditsConsumed` can still move while the lines in flight finish: read the operation 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                                                         |
| ------ | -------------------------------- | --------------------------------------------------------------- |
| `404`  | `LIST_OPERATION_NOT_FOUND`       | No operation with this id on this List of your workspace        |
| `409`  | `LIST_OPERATION_NOT_CANCELLABLE` | The operation already ended: there is nothing left to cancel    |

## Code examples

**Code Examples**

```bash [cURL]
curl -X POST https://api.reversecontact.com/v2/lists/3f6c2a8e-1b4d-4f0a-9c7e-5d2b8a1e6f40/operations/c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f/cancel \
  -H "Authorization: Bearer YOUR_API_KEY"
```
```javascript [JavaScript]
const listId = "3f6c2a8e-1b4d-4f0a-9c7e-5d2b8a1e6f40";
const operationId = "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f";
const response = await fetch(
  `https://api.reversecontact.com/v2/lists/${listId}/operations/${operationId}/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} lines not reached`);
```
```python [Python]
import requests

list_id = "3f6c2a8e-1b4d-4f0a-9c7e-5d2b8a1e6f40"
operation_id = "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f"
response = requests.post(
    f"https://api.reversecontact.com/v2/lists/{list_id}/operations/{operation_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": {
    "operationId": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "listId": "3f6c2a8e-1b4d-4f0a-9c7e-5d2b8a1e6f40",
    "kind": "list_intake_persons",
    "status": "cancelled",
    "clientAction": "done",
    "progress": {
      "processed": 120,
      "total": 500,
      "succeeded": 114,
      "failed": 6,
      "skipped": 0,
      "remaining": 380,
      "unknownCount": 0
    },
    "creditsConsumed": 456,
    "creditsPending": 0,
    "creditsEstimated": 0,
    "maxCredits": 2000,
    "estimatedSeconds": 0,
    "estimateBlockedUntil": null,
    "pollAfterSeconds": 0,
    "pollUrl": "/v2/lists/3f6c2a8e-1b4d-4f0a-9c7e-5d2b8a1e6f40/operations/c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f/progress",
    "followUp": {
      "tool": "get_list_operation",
      "arguments": {
        "listId": "3f6c2a8e-1b4d-4f0a-9c7e-5d2b8a1e6f40",
        "operationId": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f"
      },
      "pollAfterSeconds": 0
    },
    "replay": { "reuseOperationId": true, "startNewWork": false, "debitAgain": false },
    "stopReason": "cancelled",
    "nextStep": {
      "action": "none",
      "message": "Cancelled. Rows that had not started were not charged."
    },
    "errorMessage": null,
    "startedAt": "2026-10-28T09:16:00.000Z",
    "completedAt": "2026-10-28T09:18:12.000Z",
    "createdAt": "2026-10-28T09:15:58.000Z",
    "updatedAt": "2026-10-28T09:18:12.000Z"
  },
  "error": null,
  "quotas": {
    "creditsConsumed": 0,
    "workspace": {
      "id": "ws_abc123",
      "credits": { "total": 10000, "used": 4978, "left": 5022 }
    }
  },
  "metadata": {
    "requestId": "2373b182-0821-45fb-b10f-37428d3b8087",
    "executionTimeMs": 40
  }
}
```
```json [409 - Already ended]
{
  "success": false,
  "data": null,
  "error": {
    "code": "LIST_OPERATION_NOT_CANCELLABLE",
    "message": "This List operation already ended (completed): there is nothing left to cancel"
  },
  "quotas": {
    "creditsConsumed": 0,
    "workspace": {
      "id": "ws_abc123",
      "credits": { "total": 10000, "used": 4978, "left": 5022 }
    }
  },
  "metadata": {
    "requestId": "d5f5c787-9f5f-42c4-9de8-f94144955cec",
    "executionTimeMs": 22
  }
}
```
