> ## 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.

# Create KPI

> Add a new KPI definition to an existing dataset

# Create KPI

Add a new KPI (question) definition to a dataset. Each KPI defines what data owners will be asked during campaigns.

## Request

### Headers

<ParamField header="x-api-key" type="string" required>
  Your API key for authentication

  **Example:** `sk_live_1234567890abcdef`
</ParamField>

<ParamField header="x-organization-id" type="string" required>
  Your organization UUID

  **Example:** `a8315ef3-dd50-43f8-b7ce-d839e68d51fa`
</ParamField>

### Path Parameters

<ParamField path="dataset_id" type="string" required>
  UUID of the parent dataset

  **Example:** `"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`
</ParamField>

### Body Parameters

<ParamField body="name" type="string" required>
  KPI name (1–255 characters)

  **Example:** `"Monthly electricity consumption"`
</ParamField>

<ParamField body="description" type="string">
  KPI description (max 2000 characters)

  **Example:** `"Total kWh consumed from all sources"`
</ParamField>

<ParamField body="unit" type="string">
  Measurement unit (max 50 characters)

  **Example:** `"kWh"`, `"m³"`, `"tonnes"`
</ParamField>

<ParamField body="value_type" type="string" default="number">
  Expected answer type. One of: `number`, `text`, `percentage`, `date`, `boolean`, `select`
</ParamField>

<ParamField body="required" type="boolean" default="true">
  Whether a response value is mandatory
</ParamField>

<ParamField body="sort_order" type="integer" default="0">
  Display order within the dataset (0-based)
</ParamField>

<ParamField body="options" type="array[object]">
  **Required for `select` type.** List of selectable options (2–100). Each object has:

  | Field        | Type    | Required | Description                                                 |
  | ------------ | ------- | -------- | ----------------------------------------------------------- |
  | `label`      | string  | Yes      | Option label (1–255 chars, unique per KPI case-insensitive) |
  | `sort_order` | integer | No       | Display order (default `0`)                                 |

  Options are forbidden for non-select types.
</ParamField>

## Response

Returns the created KPI definition (HTTP 201).

<ResponseField name="id" type="string">
  KPI UUID.
</ResponseField>

<ResponseField name="dataset_id" type="string">
  Parent dataset UUID.
</ResponseField>

<ResponseField name="name" type="string">
  KPI display name.
</ResponseField>

<ResponseField name="description" type="string | null">
  KPI description.
</ResponseField>

<ResponseField name="unit" type="string | null">
  Measurement unit (e.g. `m³`, `kWh`).
</ResponseField>

<ResponseField name="value_type" type="string">
  Data type: `number`, `text`, `percentage`, `date`, `boolean`, or `select`.
</ResponseField>

<ResponseField name="required" type="boolean">
  Whether a response value is mandatory.
</ResponseField>

<ResponseField name="sort_order" type="integer">
  Display order within the dataset.
</ResponseField>

<ResponseField name="options" type="array[object]">
  Available choices for `select`-type KPIs. Each has `id`, `label`, and `sort_order`.
</ResponseField>

<ResponseField name="created_at" type="datetime">
  Timestamp when the KPI was created
</ResponseField>

<ResponseField name="updated_at" type="datetime | null">
  Timestamp when the KPI was last updated
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST "https://api.dcycle.io/v1/custom-kpi-datasets/${DATASET_ID}/kpis" \
    -H "x-api-key: ${DCYCLE_API_KEY}" \
    -H "x-organization-id: ${DCYCLE_ORG_ID}" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Monthly electricity consumption",
      "unit": "kWh",
      "value_type": "number",
      "required": true,
      "sort_order": 0
    }'
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import requests
  import os

  headers = {
      "x-api-key": os.getenv("DCYCLE_API_KEY"),
      "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
      "Content-Type": "application/json",
  }

  dataset_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

  response = requests.post(
      f"https://api.dcycle.io/v1/custom-kpi-datasets/{dataset_id}/kpis",
      headers=headers,
      json={
          "name": "Monthly electricity consumption",
          "unit": "kWh",
          "value_type": "number",
          "required": True,
          "sort_order": 0,
      },
  )

  kpi = response.json()
  print(f"Created KPI: {kpi['id']} ({kpi['value_type']})")
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const axios = require('axios');

  const headers = {
    'x-api-key': process.env.DCYCLE_API_KEY,
    'x-organization-id': process.env.DCYCLE_ORG_ID,
    'Content-Type': 'application/json',
  };

  const datasetId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

  axios.post(`https://api.dcycle.io/v1/custom-kpi-datasets/${datasetId}/kpis`, {
    name: 'Monthly electricity consumption',
    unit: 'kWh',
    value_type: 'number',
    required: true,
    sort_order: 0,
  }, { headers })
  .then(response => console.log(`Created KPI: ${response.data.id}`));
  ```
</CodeGroup>

### Successful Response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "dataset_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Monthly electricity consumption",
  "description": null,
  "unit": "kWh",
  "value_type": "number",
  "required": true,
  "sort_order": 0,
  "options": [],
  "created_at": "2025-03-10T14:00:00Z",
  "updated_at": null
}
```

### Select Type Example

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.dcycle.io/v1/custom-kpi-datasets/${DATASET_ID}/kpis" \
  -H "x-api-key: ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Primary energy source",
    "value_type": "select",
    "options": [
      {"label": "Grid electricity", "sort_order": 0},
      {"label": "Solar PV", "sort_order": 1},
      {"label": "Natural gas", "sort_order": 2},
      {"label": "Other", "sort_order": 3}
    ]
  }'
```

## Common Errors

### 401 Unauthorized

**Cause:** Missing or invalid API key

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"detail": "Invalid API key for organization", "code": "INVALID_API_KEY"}
```

### 403 Forbidden

**Cause:** The authenticated user is not a member of the organization

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"detail": "Logged User is not Member of Organization", "code": "LOGGED_USER_NOT_MEMBER"}
```

### 422 Validation Error

**Cause:** Select KPI missing options

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": [
    {
      "loc": ["body", "__root__"],
      "msg": "A select KPI must define options.",
      "type": "value_error"
    }
  ]
}
```

**Cause:** Options provided for a non-select type

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": [
    {
      "loc": ["body", "__root__"],
      "msg": "Only select-type KPIs can define options.",
      "type": "value_error"
    }
  ]
}
```

### 404 Not Found

**Cause:** Dataset not found

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": "Dataset not found"
}
```

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Update KPI" icon="pencil" href="/api-reference/custom-kpi/update-kpi">
    Modify a KPI definition
  </Card>

  <Card title="Delete KPI" icon="trash" href="/api-reference/custom-kpi/delete-kpi">
    Remove a KPI from the dataset
  </Card>
</CardGroup>
