Skip to main content

Supply Chain Management Use Case

Record your suppliers, products, origins and orders in Coolset so they can be traced and assessed.

Overview​

The Supply Chain API holds the data Coolset needs for traceability and compliance (EUDR, EUTR, PPWR):

  • Value chain partners: your suppliers and customers
  • Origins: the plots or locations where commodities are produced
  • Products: what you buy, produce or manufacture
  • Orders: purchase and sale orders, with their line items

All supply chain endpoints are served from:

https://developers-scranton.coolset.com/api

Supply Chain Flow​

Workflow: Record a Purchase​

1. Add the supplier​

curl -X POST https://developers-scranton.coolset.com/api/value-chains/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_name": "Cooperativa Café Sul",
"type": "supplier",
"country_code": "BR",
"contact_name": "Ana Souza",
"contact_email": "[email protected]",
"external_id": "SUP-0042"
}'

Required: company_name, type, country_code, contact_name, contact_email. Set "send_information_request": true to invite the supplier to add their own data.

2. Add the product​

curl -X POST https://developers-scranton.coolset.com/api/products/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Arabica green coffee",
"sku": "COFFEE-AR-001",
"type": "purchased",
"composition": "simple",
"commodity": "coffee",
"hs_code": "090111",
"external_id": "ERP-55120"
}'
  • type: purchased, produced or manufactured
  • composition: simple, or composite for products made of other products (list them in product_components)
  • commodity: cattle, cocoa, coffee, palm_oil, rubber, soya or wood

3. Add the origin​

location is GeoJSON: a Point or Polygon, optionally wrapped in a Feature or FeatureCollection. Coordinates are [longitude, latitude] and must fall inside country_code.

curl -X POST https://developers-scranton.coolset.com/api/origins/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Fazenda Boa Vista, plot 3",
"country_code": "BR",
"location": {
"type": "Point",
"coordinates": [-45.4321, -21.2345]
},
"external_id": "PLOT-BV-3"
}'

4. Create the purchase order with its items​

POST /orders/purchase/ creates the order and its line items in one call. Your company is set as the buyer.

curl -X POST https://developers-scranton.coolset.com/api/orders/purchase/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "order",
"external_id": "PO-2025-0042",
"order_created_at": "2025-07-01",
"value_chain": 806,
"items": [
{
"buyer_product": 88430,
"volume": 12000,
"unit": "kg",
"external_id": "PO-2025-0042-1"
}
]
}'
  • value_chain: the supplier's ID from step 1
  • items[].buyer_product: your product's ID from step 2
  • items[].volume and items[].unit are required for each item

Use POST /orders/sale/ for sale orders (your company is the seller) and POST /orders/batch/ to create many orders in one request.

5. Update the order​

Use the same route type you created it with. To change the arrival date and the item volume:

curl -X PATCH https://developers-scranton.coolset.com/api/orders/purchase/193/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"order_arrival_at": "2025-08-15",
"items": [
{ "id": 322, "volume": 11800, "unit": "kg" }
]
}'
items replaces the full list

When you send items, it becomes the order's complete item list:

  • Items matched by id (or by external_id when no id is given) are updated.
  • Items without a match are created.
  • Existing items you leave out are deleted.

To change only order fields, leave items out of the request.

Use PATCH /orders/sale/{id}/ for sale orders.

Reading Orders​

List orders​

order_action is required on order lists and statistics:

  • buying: orders where your company is the buyer
  • selling: orders where your company is the seller
curl "https://developers-scranton.coolset.com/api/orders/?order_action=buying&assessment_status=failing&limit=50" \
-H "Authorization: ApiKey YOUR_API_TOKEN"

Useful filters: assessment_status, buyer_company_status, seller_company_status, type, order_created_at__gte, order_created_at__lte, items__external_id, search, ordering.

Get one order​

curl "https://developers-scranton.coolset.com/api/orders/193/?order_action=buying" \
-H "Authorization: ApiKey YOUR_API_TOKEN"

Order detail needs the same order_action as the list (buying or selling). Without it you get 400.

The response includes items, assessment_status, assessment_summary and pulse_params.identifier. Use the identifier to look up the order's risk assessment in the Compliance API.

List an order's items​

curl "https://developers-scranton.coolset.com/api/orders/193/order-items/?order_action=buying" \
-H "Authorization: ApiKey YOUR_API_TOKEN"

Statistics​

Orders per month, by type​

curl "https://developers-scranton.coolset.com/api/orders/statistics/?order_action=buying&group_by=type&delimit_by=monthly&order_created_at__gte=2025-01-01" \
-H "Authorization: ApiKey YOUR_API_TOKEN"
[
{ "date": "2025-01-01", "type": "order", "order_count": 3 },
{ "date": "2025-07-01", "type": "order", "order_count": 7 },
{ "date": "2025-10-01", "type": "product_chain", "order_count": 1 }
]

group_by accepts type, buyer_company_status, seller_company_status, assessment_status and value_chain_id. delimit_by accepts daily, monthly and yearly. Statistics are not paginated.

Products by commodity​

curl "https://developers-scranton.coolset.com/api/products/statistics/?group_by=commodity" \
-H "Authorization: ApiKey YOUR_API_TOKEN"
[
{ "commodity": "wood", "product_count": 1691 },
{ "commodity": "cattle", "product_count": 32 },
{ "commodity": null, "product_count": 123 }
]

Complete Example: Python​

import requests

BASE_URL = "https://developers-scranton.coolset.com/api"
HEADERS = {
"Authorization": "ApiKey YOUR_API_TOKEN",
"Content-Type": "application/json",
}


def post(path, body):
response = requests.post(f"{BASE_URL}{path}", headers=HEADERS, json=body)
response.raise_for_status()
return response.json()


def record_purchase(supplier, product, po_number, volume_kg):
value_chain = post("/value-chains/", {
"company_name": supplier["name"],
"type": "supplier",
"country_code": supplier["country_code"],
"contact_name": supplier["contact_name"],
"contact_email": supplier["contact_email"],
"external_id": supplier["external_id"],
})

product = post("/products/", {
"name": product["name"],
"sku": product["sku"],
"type": "purchased",
"composition": "simple",
"commodity": product["commodity"],
})

return post("/orders/purchase/", {
"type": "order",
"external_id": po_number,
"order_created_at": "2025-07-01",
"value_chain": value_chain["id"],
"items": [
{"buyer_product": product["id"], "volume": volume_kg, "unit": "kg"},
],
})


def failing_purchase_orders():
"""Yield every purchase order whose assessment is failing."""
url = f"{BASE_URL}/orders/"
params = {"order_action": "buying", "assessment_status": "failing", "limit": 100}
while url:
response = requests.get(url, headers=HEADERS, params=params)
response.raise_for_status()
page = response.json()
yield from page["results"]
url, params = page["next"], None # `next` already carries the query

Best Practices​

Use your own IDs​

Set external_id on partners, products, origins, orders and items. You can then filter by it (for example ?external_id=ERP-55120 on products, or ?items__external_id=PO-2025-0042-1 on orders) instead of storing Coolset IDs.

Sync changes instead of re-creating​

Look records up by external_id and PATCH them when they change. Creating duplicates splits assessment history.

Poll for assessment changes​

Coolset does not send webhooks yet. To react to assessment changes, poll orders with assessment_status filters, or read risk assessments as shown in the EUDR guide.

API Endpoints Used​

Next Steps​

Call +31 20 2101245 on FaceTime