curl -X POST "https://api.dcycle.io/v2/purchases/suppliers" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 6b5a4f3e-2d1c-4b0a-9f8e-7d6c5b4a3f2e" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"business_name": "Papelera del Norte SL",
"country": "ES",
"code": "1712",
"framework": "cnae_2009",
"client_row_id": "SUP-0042"
},
{
"business_name": "Transportes Ribera SA",
"country": "es"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/suppliers",
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={
"records": [
{
"business_name": "Papelera del Norte SL",
"country": "ES",
"code": "1712",
"framework": "cnae_2009",
"client_row_id": "SUP-0042",
},
{"business_name": "Transportes Ribera SA", "country": "es"},
]
},
timeout=60,
)
response.raise_for_status()
print([item["supplier"]["id"] for item in response.json()["items"]])
{
"items": [
{
"source_row_index": 0,
"client_row_id": "SUP-0042",
"supplier": {
"id": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c",
"organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
"business_name": "Papelera del Norte SL",
"country": "ES",
"status": "active",
"created_at": "2026-10-20T09:12:44.031Z"
}
},
{
"source_row_index": 1,
"client_row_id": null,
"supplier": {
"id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
"organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
"business_name": "Transportes Ribera SA",
"country": "ES",
"status": "in_progress",
"created_at": "2026-10-20T09:12:44.031Z"
}
}
]
}
{
"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": "SUP-0042",
"error_code": "SUPPLIER_ALREADY_EXISTS",
"error_params": {
"field": "business_name",
"supplier_id": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"
}
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
Create Purchase Suppliers (v2)
Create up to 1,000 purchase suppliers in one request, classified at once when you send their activity code
POST
/
v2
/
purchases
/
suppliers
curl -X POST "https://api.dcycle.io/v2/purchases/suppliers" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 6b5a4f3e-2d1c-4b0a-9f8e-7d6c5b4a3f2e" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"business_name": "Papelera del Norte SL",
"country": "ES",
"code": "1712",
"framework": "cnae_2009",
"client_row_id": "SUP-0042"
},
{
"business_name": "Transportes Ribera SA",
"country": "es"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/suppliers",
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={
"records": [
{
"business_name": "Papelera del Norte SL",
"country": "ES",
"code": "1712",
"framework": "cnae_2009",
"client_row_id": "SUP-0042",
},
{"business_name": "Transportes Ribera SA", "country": "es"},
]
},
timeout=60,
)
response.raise_for_status()
print([item["supplier"]["id"] for item in response.json()["items"]])
{
"items": [
{
"source_row_index": 0,
"client_row_id": "SUP-0042",
"supplier": {
"id": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c",
"organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
"business_name": "Papelera del Norte SL",
"country": "ES",
"status": "active",
"created_at": "2026-10-20T09:12:44.031Z"
}
},
{
"source_row_index": 1,
"client_row_id": null,
"supplier": {
"id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
"organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
"business_name": "Transportes Ribera SA",
"country": "ES",
"status": "in_progress",
"created_at": "2026-10-20T09:12:44.031Z"
}
}
]
}
{
"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": "SUP-0042",
"error_code": "SUPPLIER_ALREADY_EXISTS",
"error_params": {
"field": "business_name",
"supplier_id": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"
}
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
← Purchases API
Add the suppliers your organization buys from before you send their purchases: a name, a country and, if you know
it, the supplier’s activity code (CNAE or SIC), which picks the spend-based emission factor of its purchases.
Nothing is calculated, so the request is synchronous (see
how bulk ingestion works): it answers
Request-level errors:
201 Created with the created suppliers.
It also records an ingest job, born completed, that sends no webhook.
A supplier is the one Create Purchases (v2) uses for a purchase sent with its
supplier_id, or with the same supplier_name and supplier_country. This route sends no email and calculates
no emissions: those come with the purchases.
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" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 6b5a4f3e-2d1c-4b0a-9f8e-7d6c5b4a3f2e" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"business_name": "Papelera del Norte SL",
"country": "ES",
"code": "1712",
"framework": "cnae_2009",
"client_row_id": "SUP-0042"
},
{
"business_name": "Transportes Ribera SA",
"country": "es"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/purchases/suppliers",
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={
"records": [
{
"business_name": "Papelera del Norte SL",
"country": "ES",
"code": "1712",
"framework": "cnae_2009",
"client_row_id": "SUP-0042",
},
{"business_name": "Transportes Ribera SA", "country": "es"},
]
},
timeout=60,
)
response.raise_for_status()
print([item["supplier"]["id"] for item in response.json()["items"]])
{
"items": [
{
"source_row_index": 0,
"client_row_id": "SUP-0042",
"supplier": {
"id": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c",
"organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
"business_name": "Papelera del Norte SL",
"country": "ES",
"status": "active",
"created_at": "2026-10-20T09:12:44.031Z"
}
},
{
"source_row_index": 1,
"client_row_id": null,
"supplier": {
"id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
"organization_id": "a8315ef3-dd50-43f8-b7ce-d839e68d51fa",
"business_name": "Transportes Ribera SA",
"country": "ES",
"status": "in_progress",
"created_at": "2026-10-20T09:12:44.031Z"
}
}
]
}
{
"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": "SUP-0042",
"error_code": "SUPPLIER_ALREADY_EXISTS",
"error_params": {
"field": "business_name",
"supplier_id": "5f4e3d2c-1b0a-4f9e-8d7c-6b5a4f3e2d1c"
}
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
How it works
- Every record is checked: its name and country are free, and its
code, if sent, is an active code of itsframework. If any record is invalid the whole request is rejected with422and nothing is created (all or nothing):rejectedis the number of invalid records anderrorslists them (up to 100). - The name and country identify a supplier in the organization, with the key
Create Purchases (v2) matches
supplier_nameby: the exact name (case, spaces and punctuation count; commas become dots) and the country, in upper case. A supplier already stored with that key, whatever its status, rejects the record (SUPPLIER_ALREADY_EXISTS, with its id): an existing supplier is never reclassified here, because its purchases would need recalculating. Two records with the same key reject the second (DUPLICATE_SUPPLIER), also when they differ only by commas and dots or the case of the country. - Every supplier is created in one transaction and returned, in the order sent.
source_row_indexis the position of the record inrecords, andclient_row_idechoes yours. The stored name has its commas replaced by dots, and the country is upper-cased.- With
codeandframework, the supplier is classified at once: it isactive, and its purchases are calculated with the factor of that activity and its country, without a D&B lookup. - Without, it is created exactly as Create Purchases (v2) creates a supplier it does not know: if Dcycle
already knows an official (D&B) classification for that exact name and country, the supplier takes it; otherwise
it is
in_progressuntil its first purchase classifies it (the 80 % rule of Create Purchases (v2)).
- With
- The suppliers belong to the
x-organization-idorganization. A record cannot name another one, not even one of your perimeter: send each organization’s suppliers with its ownx-organization-id.
supplier_name and supplier_country, or with the supplier’s id as
supplier_id, uses it. To retry a request safely, repeat it with the same Idempotency-Key.
Activity codes
GET /v1/purchases/suppliers/codes?framework=cnae_2009 (or us_sic_v87), with your x-api-key and
x-organization-id, lists the codes of a framework, as strings (["0111", "0130", ...]). A code is accepted when it
is an active code of its framework in Dcycle’s classification; any other is REFERENCE_NOT_FOUND.
For
us_sic_v87, the list can include codes that are not active, which this route rejects with
REFERENCE_NOT_FOUND. Prefer cnae_2009 codes, or leave code out and let Dcycle classify the supplier.Request
Headers
string
required
Your API key (see Authentication).
string
required
The organization the suppliers belong to.
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 answers the same suppliers (with
Idempotent-Replayed: true) instead of
creating them again; 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 invalid records, nothing written; 200 with
{"dry_run": true, "submitted": n, "valid": n} when every record is valid. It does not check or store the
Idempotency-Key.Body Parameters
array[object]
required
The suppliers to create, 1 to 1,000. A field not listed here is rejected (
SCHEMA_INVALID).Show record
Show record
string
required
The supplier’s legal name, 1 to 255 characters, spelt as your purchases will send it in
supplier_name.string
required
The supplier’s country: two letters, in upper or lower case (stored in upper case), not checked against ISO
3166-1, so send its ISO 3166-1 alpha-2 code (
ES, FR…). It picks the country of the spend-based factor.string
The supplier’s activity code in
framework, up to 16 characters (see Activity codes). Send it
with framework. Omitted: Dcycle classifies the supplier.string
The classification of
code: cnae_2009 (CNAE 2009) or us_sic_v87 (US SIC 1987). Send it with code.string
Your own reference for the record, up to 255 characters, echoed in errors and in the answer.
Response
201 Created with items: one per record, in the order sent, with its source_row_index, client_row_id and the
created supplier:
id: the id to send assupplier_idin Create Purchases (v2), and the one Delete Purchase Suppliers (v2) takes.organization_id,business_name(as stored),country(upper case),created_at.status:activewhen the supplier is classified,in_progresswhile Dcycle classifies it.
code and
code_type.
Errors
Problem details. Per-record codes inerrors[].error_code of a 422 BULK_RECORDS_REJECTED:
error_code | Cause |
|---|---|
SCHEMA_INVALID | The record does not match the schema: no business_name or country, a country that is not two letters, code without framework or the other way round, a framework not listed, an unknown field (e.g. organization_id or status)… error_params.errors says where. |
SUPPLIER_ALREADY_EXISTS | The organization already has a supplier with this name and country (commas as dots, country case aside). error_params.supplier_id is its id: use it as supplier_id. |
DUPLICATE_SUPPLIER | Another record of the request has the same name and country (commas as dots, country case aside). |
REFERENCE_NOT_FOUND | code is not an active code of framework (error_params.field is code). |
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 records, more than 1,000, no
Idempotency-Key), 422 IDEMPOTENCY_KEY_REUSED, 409 IDEMPOTENCY_KEY_IN_PROGRESS, and 429 beyond the
rate limits.
Limits
| Limit | Value |
|---|---|
| Records per request | 1 to 1,000 |
Related Endpoints
Delete Purchase Suppliers (v2)
Delete suppliers by id
Create Purchases (v2)
Purchases from these suppliers
List Purchase Suppliers
The suppliers stored, with their activity code
List Ingest Jobs
The job each request records
Was this page helpful?