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,producedormanufacturedcomposition:simple, orcompositefor products made of other products (list them inproduct_components)commodity:cattle,cocoa,coffee,palm_oil,rubber,soyaorwood
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 1items[].buyer_product: your product's ID from step 2items[].volumeanditems[].unitare 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 listWhen you send items, it becomes the order's complete item list:
- Items matched by
id(or byexternal_idwhen noidis 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 buyerselling: 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
POST /value-chains/- Supply Chain APIPOST /products/- Supply Chain APIPOST /origins/- Supply Chain APIPOST /orders/purchase/,PATCH /orders/purchase/{id}/- Supply Chain APIGET /orders/,GET /orders/{id}/,GET /orders/{order_pk}/order-items/- Supply Chain APIGET /orders/statistics/,GET /products/statistics/- Supply Chain API