---
title: "Cancel a bulk request"
method: POST
path: "/bulk_requests/{id}/cancel"
---

# Cancel a bulk request

`POST /bulk_requests/{id}/cancel`

Sets `cancellation_requested_at` so the running job exits cleanly to `cancelled` on its next heartbeat. Idempotent — re-cancelling just bumps the timestamp.

## Path parameters

- `id` string, required

## Response `202`

Cancellation requested

- BulkRequest — Full bulk request payload, returned by show, list, cancel, and retry endpoints.
  - `id` integer
  - `status` 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled' — pending = queued, processing = records being inserted, completed = all rows processed, failed = job errored, cancelled = cancellation honored on a heartbeat
  - `source` string, nullable — Origin of the request (e.g. `bulk_api`, `csv`, `nacha`)
  - `total_items` integer
  - `processed_items` integer
  - `successful_items` integer
  - `failed_items` integer
  - `skipped_items` integer — Records skipped due to `on_conflict: skip` matching an existing `external_id`
  - `progress` integer — processed_items / total_items as a percentage (0-100)
  - `filename` string, nullable — Original filename for CSV/NACHA uploads. Null for API-submitted requests.
  - `created_at` string, date-time
  - `started_at` string, date-time, nullable
  - `completed_at` string, date-time, nullable
  - `last_progress_at` string, date-time, nullable — Heartbeat timestamp from the worker; used to detect stuck jobs
  - `cancellation_requested_at` string, date-time, nullable — Set by POST /bulk_requests/{id}/cancel; the worker exits at its next heartbeat
  - `parsing` boolean — True while a CSV/NACHA upload is still being parsed (before rows are enqueued)
  - `stuck` boolean — True if the request has been `processing` for more than 5 minutes without a heartbeat
  - `error_message` string, nullable
  - `error_details` object — Free-form structured error context (e.g. row indices, validation errors)
  - `parent_bulk_request_id` integer, nullable — If this request is a retry, the ID of the original (parent) bulk request
  - `on_conflict` 'skip' | 'error' — Conflict policy applied when an `external_id` already exists in the stream
  - `data_stream_id` integer, nullable — Data stream the request targets, when applicable

## Other responses

- `401` — Unauthorized
- `404` — Bulk request not found
- `409` — Bulk request is not in a cancellable status (must be `pending` or `processing`)

---

[API](https://skmtc.net/endclose/apis/end-close-api.md) · [All operations](https://skmtc.net/endclose/apis/end-close-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/endclose/end-close-api/revisions/613ceff68dc7/schema)
