> ## Documentation Index
> Fetch the complete documentation index at: https://code.dcycle.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 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

Retrieve the custom emission groups available to your organization, with the units each one accepts. This is where you resolve the `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.

<Warning>
  **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.
</Warning>

<Note>
  **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.
</Note>

<Note>
  **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.
</Note>

## Request

### Headers

<ParamField header="x-organization-id" type="string" required>
  UUID of the organization whose groups you want. The response is scoped to it.

  **Format:** UUID
</ParamField>

<ParamField header="x-api-key" type="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.
</ParamField>

### Query Parameters

<ParamField query="category" type="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`, `water`

  <Warning>
    **`waste` 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.
  </Warning>
</ParamField>

## Response

<ResponseField name="array" type="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.

  <Expandable title="Custom Emission Group Object">
    <ResponseField name="id" type="string">
      Group id (UUID). This is the value to send as `custom_emission_factor_id`.
    </ResponseField>

    <ResponseField name="name" type="string">
      Group name, as it was uploaded.
    </ResponseField>

    <ResponseField name="units" type="array[object]">
      The units this group has factors for. **May be empty** when the group has no enabled factors yet.

      <Expandable title="Unit Object">
        <ResponseField name="id" type="string">
          Unit id (UUID), the same ids served by the unit catalog.
        </ResponseField>

        <ResponseField name="name" type="string">
          Unit name, e.g. `kilogram_(kg)`.
        </ResponseField>

        <ResponseField name="type" type="string">
          Physical family of the unit, e.g. `mass`.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="parent" type="object | null">
      The parent organization a group is inherited from, or `null` when the group belongs to the organization you queried. Groups defined higher up in a holding are listed here too, with the ancestor that owns them.

      <Expandable title="Parent Object">
        <ResponseField name="id" type="string">
          Parent organization id.
        </ResponseField>

        <ResponseField name="name" type="string">
          Parent organization name.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<Warning>
  **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.
</Warning>

## Example

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  # 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"
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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")
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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");
  ```
</CodeGroup>

### Successful Response

Returns `200 OK` with the array of groups.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
[
  {
    "id": "4f8b2c1e-9a34-4d7b-8e21-0c5f6a9b3d84",
    "name": "My supplier factors",
    "units": [
      {
        "id": "2b7d4e19-5c83-4a26-9f14-6d8e0b2a7c35",
        "name": "kilogram_(kg)",
        "type": "mass"
      }
    ],
    "parent": null
  }
]
```

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.

## Common Errors

### 422 Unprocessable Entity

**Cause:** `x-organization-id` is missing.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": [
    {
      "loc": ["header", "x-organization-id"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}
```

**Cause:** `x-organization-id` is present but is not a valid UUID.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": [
    {
      "loc": ["header", "x-organization-id"],
      "msg": "value is not a valid uuid",
      "type": "type_error.uuid"
    }
  ]
}
```

A misspelled `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:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
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,
)
```

Add `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 the `category` 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

<CardGroup cols={2}>
  <Card title="Create Purchase" icon="cart-shopping" href="/api-reference/purchases/create">
    Accepts `custom_emission_factor_id` to calculate with your own factors
  </Card>

  <Card title="Create Invoice" icon="file-invoice" href="/api-reference/invoices/create">
    Same field, for energy and utility invoices
  </Card>

  <Card title="Create Waste" icon="recycle" href="/api-reference/wastes/create">
    Same field, for waste records
  </Card>

  <Card title="List Units" icon="ruler" href="/api-reference/units/list">
    The shared unit catalog the `units` ids come from
  </Card>
</CardGroup>
