List Custom Emission Groups
const options = {method: 'GET', headers: {'x-organization-id': '<x-organization-id>'}};
fetch('https://api.dcycle.io/custom_emission_groups/light', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.dcycle.io/custom_emission_groups/light"
headers = {"x-organization-id": "<x-organization-id>"}
response = requests.get(url, headers=headers)
print(response.text)curl --request GET \
--url https://api.dcycle.io/custom_emission_groups/light \
--header 'x-organization-id: <x-organization-id>'{
"array": {
"id": "<string>",
"name": "<string>",
"units": {
"id": "<string>",
"name": "<string>",
"type": "<string>"
},
"parent": {
"id": "<string>",
"name": "<string>"
}
}
}List Custom Emission Groups
Resolve the custom_emission_factor_id required when a record is calculated with your own emission factors instead of a database factor
GET
/
custom_emission_groups
/
light
List Custom Emission Groups
const options = {method: 'GET', headers: {'x-organization-id': '<x-organization-id>'}};
fetch('https://api.dcycle.io/custom_emission_groups/light', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.dcycle.io/custom_emission_groups/light"
headers = {"x-organization-id": "<x-organization-id>"}
response = requests.get(url, headers=headers)
print(response.text)curl --request GET \
--url https://api.dcycle.io/custom_emission_groups/light \
--header 'x-organization-id: <x-organization-id>'{
"array": {
"id": "<string>",
"name": "<string>",
"units": {
"id": "<string>",
"name": "<string>",
"type": "<string>"
},
"parent": {
"id": "<string>",
"name": "<string>"
}
}
}Retrieve the custom emission groups available to your organization, with the units each one accepts. This is where you resolve the
The group id above is illustrative: groups belong to your organization, so yours will differ. The unit ids, in contrast, come from the shared unit catalog.
Cause:
A misspelled
Add
custom_emission_factor_id that invoices, purchases, wastes and vehicles expect when a record is calculated with your own emission factors instead of one from a public database.
The field is called
custom_emission_factor_id, but it takes a GROUP id — the one returned by this endpoint. There is no custom_emission_group_id field anywhere: invoices, purchases, wastes and vehicles all name it custom_emission_factor_id while the foreign key points at custom_emission_group.id. Sending a factor id instead of a group id is the most common mistake here, and the name is why.Organization-scoped, not a global catalog. Unlike units or waste codes, these groups belong to your organization — the ids are yours and differ between organizations. Fetch them per organization rather than hardcoding them.
This route carries no version prefix. It is
/custom_emission_groups/…, not /v1/… or /v2/…. Only the LCA endpoints share that shape; everything else in this reference is versioned. Documented as it is today.Request
Headers
string
required
UUID of the organization whose groups you want. The response is scoped to it.Format: UUID
string
Your API key.Unlike the rest of the API, this endpoint does not validate it today — it is the only header the route does not declare. Send it anyway: it keeps your client consistent with every other call and will keep working when the endpoint is brought in line.
Query Parameters
string
Return only the groups of one category. Omit it to get all of them.The eight accepted values:
purchases, electricity, waste, heat, vehicles, recharge, process, waterwaste is singular, and the filter is free text rather than a closed list — so a value that is not in the eight above does not return 422, it returns an empty array. ?category=wastes gives you zero groups on an organization that has dozens of them.That makes “no groups in that category” and “I misspelled the category” look identical. Check the spelling against the list above before concluding there is nothing there.Response
array[object]
Array of custom emission group objects. Not paginated — it returns every matching group, and on a subsidiary of a large holding that can be thousands of objects in a single response. Use
category to keep it small.Show Custom Emission Group Object
Show Custom Emission Group Object
string
Group id (UUID). This is the value to send as
custom_emission_factor_id.string
Group name, as it was uploaded.
array[object]
object | null
Listed is not the same as usable, and the two differ per category.For purchases, the calculation requires the group to belong to the same organization as the record (
ceg.organization_id = p.organization_id) and to have a factor for the unit you send. So a group with a non-null parent appears in this response and will not calculate a purchase — pick one whose parent is null, and take the unit from its own units.For vehicles, invoices and wastes the calculation does not check the unit at all: it matches on the group and the date. A unit the group has no factor for does not fail there — it calculates against whatever factor the date selects, which is worse than an error because the number looks fine.Example
# All groups available to the organization
curl -X GET "https://api.dcycle.io/custom_emission_groups/light" \
-H "x-organization-id: YOUR_ORGANIZATION_ID" \
-H "x-api-key: YOUR_API_KEY"
# Only the ones usable on a purchase
curl -X GET "https://api.dcycle.io/custom_emission_groups/light?category=purchases" \
-H "x-organization-id: YOUR_ORGANIZATION_ID" \
-H "x-api-key: YOUR_API_KEY"
import requests
HEADERS = {"x-organization-id": "YOUR_ORGANIZATION_ID", "x-api-key": "YOUR_API_KEY"}
groups = requests.get(
"https://api.dcycle.io/custom_emission_groups/light",
headers=HEADERS,
params={"category": "purchases"},
timeout=30,
).json()
# For purchases: own groups only (parent is null) and with at least one unit
usable = [g for g in groups if g["parent"] is None and g["units"]]
group = next(g for g in usable if g["name"] == "My supplier factors")
const params = new URLSearchParams({ category: "purchases" });
const response = await fetch(
`https://api.dcycle.io/custom_emission_groups/light?${params}`,
{
headers: {
"x-organization-id": "YOUR_ORGANIZATION_ID",
"x-api-key": "YOUR_API_KEY",
},
},
);
const groups = await response.json();
const usable = groups.filter((g) => g.parent === null && g.units.length > 0);
const group = usable.find((g) => g.name === "My supplier factors");
Successful Response
Returns200 OK with the array of groups.
[
{
"id": "4f8b2c1e-9a34-4d7b-8e21-0c5f6a9b3d84",
"name": "My supplier factors",
"units": [
{
"id": "2b7d4e19-5c83-4a26-9f14-6d8e0b2a7c35",
"name": "kilogram_(kg)",
"type": "mass"
}
],
"parent": null
}
]
Common Errors
422 Unprocessable Entity
Cause:x-organization-id is missing.
{
"detail": [
{
"loc": ["header", "x-organization-id"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
x-organization-id is present but is not a valid UUID.
{
"detail": [
{
"loc": ["header", "x-organization-id"],
"msg": "value is not a valid uuid",
"type": "type_error.uuid"
}
]
}
category produces neither of these — it returns an empty array. See the warning above.
Use Cases
Create a purchase calculated with your own factors
A purchase carries a monetary amount (quantity + the currency in unit_id) and/or a physical one (non_currency_quantity + non_currency_unit_id). Custom emission groups for purchases almost always carry physical units, so the group’s unit goes in the physical pair — putting it in unit_id would declare kilograms as a currency:
groups = requests.get(
"https://api.dcycle.io/custom_emission_groups/light",
headers=HEADERS, params={"category": "purchases"}, timeout=30,
).json()
group = next(g for g in groups if g["parent"] is None and g["units"])
requests.post(
"https://api.dcycle.io/v1/purchases",
headers=HEADERS,
json={
"expense_type": "opex",
"product_name": "Recycled paper",
"purchase_date": "2026-01-31",
"non_currency_quantity": 250.0,
"non_currency_unit_id": group["units"][0]["id"],
"custom_emission_factor_id": group["id"],
},
timeout=30,
)
quantity and unit_id on top when you also want to record what it cost. The calculation matches the factor on COALESCE(non_currency_unit_id, unit_id), so with a physical group the physical pair is what selects the factor.
Offer only the groups that apply
When your integration lets users choose, call this endpoint with thecategory of the record being created and show only those groups — filtering out inherited ones for purchases. The response also tells you which units to offer for each group.
Related Endpoints
Create Purchase
Accepts
custom_emission_factor_id to calculate with your own factorsCreate Invoice
Same field, for energy and utility invoices
Create Waste
Same field, for waste records
List Units
The shared unit catalog the
units ids come fromWas this page helpful?