curl -X POST "https://api.dcycle.io/v2/purchases/delete" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 7b6a5f4e-3d2c-4b1a-9f8e-7d6c5b4a3f2e" \
-H "Content-Type: application/json" \
-d '{"ids": ["9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d", "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/delete",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={"ids": ["9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"]},
timeout=60,
)
response.raise_for_status()
print(response.json()["id"])
{
"id": "3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f",
"status": "processing",
"entity_type": "purchases",
"operation": "delete",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T09:20:31.877Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f",
"items": "/v2/ingest-jobs/3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 1,
"client_row_id": null,
"error_code": "NOT_FOUND",
"error_params": { "field": "ids", "value": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
Delete Purchases (v2)
Delete up to 5,000 purchases by id, asynchronously, as an ingest job
POST
/
v2
/
purchases
/
delete
curl -X POST "https://api.dcycle.io/v2/purchases/delete" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 7b6a5f4e-3d2c-4b1a-9f8e-7d6c5b4a3f2e" \
-H "Content-Type: application/json" \
-d '{"ids": ["9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d", "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/delete",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={"ids": ["9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"]},
timeout=60,
)
response.raise_for_status()
print(response.json()["id"])
{
"id": "3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f",
"status": "processing",
"entity_type": "purchases",
"operation": "delete",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T09:20:31.877Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f",
"items": "/v2/ingest-jobs/3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 1,
"client_row_id": null,
"error_code": "NOT_FOUND",
"error_params": { "field": "ids", "value": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
← Purchases API
Delete purchases by id, together with their emissions. The request checks every id and answers
Request-level errors:
202 Accepted with an
ingest job that deletes them in the background. Follow it with
GET /v2/ingest-jobs/{id} or the
ingest_job.finished webhook.
The ids are the ones a create job gives you (each item’s entity_id in
GET /v2/ingest-jobs/{id}/items) or the ids of
List Purchases.
Permanent. Deleted purchases and their emissions cannot be recovered. Use
dry_run=true first if you are not sure.Beta. Part of the bulk ingest API, currently in beta. The contract may still change before general availability.
curl -X POST "https://api.dcycle.io/v2/purchases/delete" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 7b6a5f4e-3d2c-4b1a-9f8e-7d6c5b4a3f2e" \
-H "Content-Type: application/json" \
-d '{"ids": ["9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d", "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"]}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/delete",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={"ids": ["9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"]},
timeout=60,
)
response.raise_for_status()
print(response.json()["id"])
{
"id": "3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f",
"status": "processing",
"entity_type": "purchases",
"operation": "delete",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T09:20:31.877Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f",
"items": "/v2/ingest-jobs/3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 1,
"client_row_id": null,
"error_code": "NOT_FOUND",
"error_params": { "field": "ids", "value": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
How it works
- The request checks every id. One that does not exist, is outside your organization’s perimeter or is repeated
rejects the whole request with
422, and nothing is deleted. - The job deletes them in the background with their emissions, calculation data and custom column values. A purchase read from a file leaves that file settled once it has no purchase left. Deleting purchases can change which returns (negative purchases) the organization’s net can absorb, so those are recalculated.
- When every chunk is done the job is
completed(orcompleted_with_errors), andingest_job.finishedis sent with"operation": "delete".
Request
Headers
string
required
Your API key (see Authentication).
string
required
The organization the purchases are deleted for. Its purchases, and those of the organizations under it that you can
write to, can be deleted.
string
required
1 to 255 characters, unique per batch. A retry with the same key and body returns the original job; the same key
with another body is
422 IDEMPOTENCY_KEY_REUSED, and while the first request is still being processed
409 IDEMPOTENCY_KEY_IN_PROGRESS.Query Parameters
boolean
default:"false"
Check only: the same
422 for bad ids, nothing deleted, no job; 200 with {"dry_run": true, "submitted": n, "valid": n} when every id is valid.Body Parameters
array[uuid]
required
Purchase ids, 1 to 5,000, each once.
Response
202 Accepted with the job (entity_type: purchases, operation: delete) and a Location header to it.
GET /v2/ingest-jobs/{id}/items has one item per purchase, in the order sent;
an item is failed with DELETE_FAILED when its purchase was already gone by the time the job reached it.
Errors
Problem details. Per-id codes inerrors[].error_code of a 422 BULK_RECORDS_REJECTED
(source_row_index is the position in ids):
error_code | Cause |
|---|---|
NOT_FOUND | The purchase does not exist, is deleted or is outside your organization’s perimeter (all answer the same). |
DUPLICATE_ID | The purchase is already in the request. |
401 for a missing or invalid API key, 403 LOGGED_USER_NOT_MEMBER when the key’s user is not a
member of the organization, 422 REQUEST_VALIDATION_FAILED for a malformed body (no ids, more than 5,000, no
Idempotency-Key), 422 IDEMPOTENCY_KEY_REUSED, 409 IDEMPOTENCY_KEY_IN_PROGRESS, and 429 beyond the
rate limits.
Limits
| Limit | Value |
|---|---|
| Ids per request | 1 to 5,000 |
| Purchases per deletion chunk | 200 |
Related Endpoints
Create Purchases (v2)
Create purchases in bulk
List Purchases
Find the ids to delete
Get Ingest Job
Follow the deletion
List Job Items
The outcome of each id
Was this page helpful?