curl -X POST "https://api.dcycle.io/v2/purchases/suppliers/delete" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 3c2b1a09-8f7e-4d6c-9b5a-4f3e2d1c0b9a" \
-H "Content-Type: application/json" \
-d '{"ids": ["5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"]}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/suppliers/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": ["5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"]},
timeout=60,
)
response.raise_for_status()
print(response.json()["id"])
{
"id": "8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
"status": "processing",
"entity_type": "purchase_suppliers",
"operation": "delete",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-20T09:20:31.877Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
"items": "/v2/ingest-jobs/8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b/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": 0,
"client_row_id": null,
"error_code": "SUPPLIER_HAS_PURCHASES",
"error_params": { "field": "ids", "value": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c", "purchases": 3 }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 1, "valid": 1 }
Delete Purchase Suppliers (v2)
Delete up to 5,000 purchase suppliers without purchases by id, asynchronously, as an ingest job
POST
/
v2
/
purchases
/
suppliers
/
delete
curl -X POST "https://api.dcycle.io/v2/purchases/suppliers/delete" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 3c2b1a09-8f7e-4d6c-9b5a-4f3e2d1c0b9a" \
-H "Content-Type: application/json" \
-d '{"ids": ["5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"]}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/suppliers/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": ["5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"]},
timeout=60,
)
response.raise_for_status()
print(response.json()["id"])
{
"id": "8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
"status": "processing",
"entity_type": "purchase_suppliers",
"operation": "delete",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-20T09:20:31.877Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
"items": "/v2/ingest-jobs/8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b/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": 0,
"client_row_id": null,
"error_code": "SUPPLIER_HAS_PURCHASES",
"error_params": { "field": "ids", "value": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c", "purchases": 3 }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 1, "valid": 1 }
← Purchases API
Delete purchase suppliers by id. The request checks every id and answers
Request-level errors:
202 Accepted with an
ingest job that deletes them in the background (see
how bulk ingestion works). Follow it with
GET /v2/ingest-jobs/{id} or, for a job submitted with an API key, the
ingest_job.finished webhook.
The ids are the ones Create Purchase Suppliers (v2) answers, or the
ids of List Purchase Suppliers. Only a supplier with no purchases can be
deleted: delete its purchases first with Delete Purchases (v2).
Permanent. Deleted suppliers cannot be recovered. Use
dry_run=true first if you are not sure.Beta. Part of the bulk ingest API, in beta. The contract may still change before general availability.
curl -X POST "https://api.dcycle.io/v2/purchases/suppliers/delete" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 3c2b1a09-8f7e-4d6c-9b5a-4f3e2d1c0b9a" \
-H "Content-Type: application/json" \
-d '{"ids": ["5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"]}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/suppliers/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": ["5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"]},
timeout=60,
)
response.raise_for_status()
print(response.json()["id"])
{
"id": "8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
"status": "processing",
"entity_type": "purchase_suppliers",
"operation": "delete",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-20T09:20:31.877Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
"items": "/v2/ingest-jobs/8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b/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": 0,
"client_row_id": null,
"error_code": "SUPPLIER_HAS_PURCHASES",
"error_params": { "field": "ids", "value": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c", "purchases": 3 }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 1, "valid": 1 }
How it works
- The request checks every id. One that does not exist, is outside your organization’s
perimeter, is repeated or still has purchases rejects the
whole request with
422, and nothing is deleted. Disabled purchases count too. - The job deletes them in the background. A supplier that got a purchase after the request was checked is kept, and its item fails.
- When every chunk is done the job is
completed(orcompleted_with_errors). For a job submitted with an API key,ingest_job.finishedis sent with"operation": "delete"to the webhook endpoints of thex-organization-idorganization.
Request
Headers
string
required
Your API key (see Authentication).
string
required
The organization the suppliers are deleted for. Its suppliers, and those of the organizations under it that you can
write to (its perimeter), can be deleted.
string
required
1 to 255 characters, one per batch, looked up per organization across every v2 bulk route. Within 24 hours, the
same key with the same route and body returns the original job (with
Idempotent-Replayed: true); the same key with
another body or on another route is 422 IDEMPOTENCY_KEY_REUSED, and while the first request is still being
processed 409 IDEMPOTENCY_KEY_IN_PROGRESS. After 24 hours the key is free, and a request with it is a new one. See
The Idempotency-Key.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. It does not check or store the Idempotency-Key.Body Parameters
array[uuid]
required
Purchase supplier ids, 1 to 5,000, each once.
Response
202 Accepted with the job (entity_type: purchase_suppliers, operation: delete) and a Location header to it.
GET /v2/ingest-jobs/{id}/items has one item per supplier, in the order sent;
an item is failed with DELETE_FAILED when its supplier got a purchase after the request was checked, or 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 supplier does not exist or is outside your organization’s perimeter (both answer the same). |
DUPLICATE_ID | The supplier is already in the request. |
SUPPLIER_HAS_PURCHASES | The supplier still has purchases, disabled ones included. error_params.purchases is how many: delete them first with Delete Purchases (v2). |
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 |
| Suppliers per deletion chunk | 500 |
Related Endpoints
Create Purchase Suppliers (v2)
Create suppliers in bulk
List Purchase Suppliers
Find the ids to delete
Delete Purchases (v2)
Delete a supplier’s purchases first
Get Ingest Job
Follow the deletion
Was this page helpful?