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

# Get Dataset Schema

> Describe the dimensions, metrics and temporal behaviour of one dataset before querying it

Return what a dataset can be grouped by and what it can measure. [Query datasets](/api-reference/datasets/query) accepts only the dimension and metric keys listed here, so this is the contract to build a query against rather than guessing field names.

The same native key can return **different dimensions for different organizations**: the schema is extended with the custom columns your organization defined on that entity. Fetch it per organization; do not cache one organization's schema and reuse it for another.

## 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="key" type="string" required>
  Dataset key from [List Available Datasets](/api-reference/datasets/list) — a catalogue key, or `elastic:<uuid>` for a custom dataset

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

### Query Parameters

<ParamField query="framework" type="string">
  Narrow the dataset to a reporting taxonomy. It only affects datasets with framework-specific dimensions — notably `emissions` — and is a no-op elsewhere.
</ParamField>

## Response

<ResponseField name="key" type="string">
  Echo of the dataset key
</ResponseField>

<ResponseField name="label_key" type="string">
  i18n key for native datasets; the user's literal name for custom ones
</ResponseField>

<ResponseField name="kind" type="string">
  `kpi`, `activity`, `master`, `custom` or `custom_kpi`
</ResponseField>

<ResponseField name="dimensions" type="array[object]">
  <Expandable title="dimension">
    <ResponseField name="key" type="string">
      What to send in the query's `dimensions`
    </ResponseField>

    <ResponseField name="label_key" type="string">
      i18n key for the dimension name
    </ResponseField>

    <ResponseField name="field_type" type="string">
      `string`, `number`, `date`, …
    </ResponseField>

    <ResponseField name="group" type="string">
      Which entity the dimension comes from, for grouping in a picker
    </ResponseField>

    <ResponseField name="i18n_namespace" type="string | null">
      When set, the dimension's **values** are translation keys in this namespace — `gender` and `accident_context` are the workforce examples. Translate the values, not just the label.
    </ResponseField>

    <ResponseField name="relation_label" type="string | null">
      Set when the dimension is borrowed through a relation column
    </ResponseField>

    <ResponseField name="via" type="string | null">
      The relation column it was borrowed through. Advisory — the query accepts every listed dimension regardless.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="metrics" type="array[object]">
  <Expandable title="metric">
    <ResponseField name="key" type="string">
      What to send in the query's `metrics`
    </ResponseField>

    <ResponseField name="label_key" type="string">
      i18n key for the metric name
    </ResponseField>

    <ResponseField name="agg" type="string">
      How the value is aggregated across rows.

      Read it together with `row_level`. When `row_level` is `false` the metric's expression is **already** an aggregate — a count, a ratio, a wage gap — and `agg` carries its default `sum` without being applied. That is why `remuneration_avg` and `wage_gap` both report `"agg": "sum"` in the example below: the field describes the engine's aggregation step, not the metric's meaning, and for these two there is no such step.
    </ResponseField>

    <ResponseField name="decimals" type="integer">
      Suggested precision for display
    </ResponseField>

    <ResponseField name="suffix" type="string | null">
      Unit to render after the value — `h`, `%`, `t CO₂e`
    </ResponseField>

    <ResponseField name="grouping_hint_key" type="string | null">
      Dimension the metric is most meaningful grouped by
    </ResponseField>

    <ResponseField name="row_level" type="boolean">
      `false` when the metric is already an aggregate — a count, a ratio, a gap. A row-mode formula over such a metric is rejected with 400.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="default_metrics" type="array[string]">
  Metrics to preselect when nothing is chosen
</ResponseField>

<ResponseField name="window_required" type="boolean">
  `true` for native KPI and activity datasets: the query's start/end window is mandatory and drives proration. `false` for master and custom datasets, where the window is optional or ignored.
</ResponseField>

<ResponseField name="time_axis_default" type="string | null">
  The dimension that seeds the period axis
</ResponseField>

<ResponseField name="data_version" type="integer | null">
  Custom datasets only: incremented on every write, so it works as a cache key. Null for native datasets.
</ResponseField>

<Note>
  Every dataset with a date column also exposes a synthetic `period` dimension that is not part of its stored fields. It is how you group by month, quarter or year.
</Note>

## Reading the workforce schemas

`own_workforce_remuneration` is the one worth knowing in detail. Its time axis is **annual** — one row per employee per fiscal year — and its `wage_gap` metric has three traps worth repeating from [Query datasets](/api-reference/datasets/query):

* it is an FTE-weighted mean, not a simple average of salaries;
* the sign is positive when men earn more, since it is computed as `(avg_M − avg_F) / avg_M × 100`;
* it returns **null**, not `0`, for any group that lacks at least one man and one woman. Null is "not computable here", and it is not summable or averageable across groups — ask the server for subtotals instead of aggregating the numbers yourself.

It also carries fewer dimensions than `own_workforce_contracts`: organization, country, nationality, gender, job category, age and period, with no contract type, workday type, disability or labour-agreement split.

## Example

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X GET "https://api.dcycle.io/v1/datasets/own_workforce_remuneration/schema" \
    -H "x-api-key: ${DCYCLE_API_KEY}" \
    -H "x-organization-id: ${DCYCLE_ORG_ID}"
  ```

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

  import requests

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

  schema = requests.get(
      "https://api.dcycle.io/v1/datasets/own_workforce_remuneration/schema",
      headers=headers,
  ).json()

  print("group by:", [d["key"] for d in schema["dimensions"]])
  print("measure:", [m["key"] for m in schema["metrics"]])
  ```

  ```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
  };

  axios.get('https://api.dcycle.io/v1/datasets/own_workforce_remuneration/schema', { headers })
    .then(({ data }) => {
      console.log('group by:', data.dimensions.map(d => d.key));
      console.log('measure:', data.metrics.map(m => m.key));
    })
    .catch(error => console.error(error));
  ```
</CodeGroup>

### Successful Response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "key": "own_workforce_remuneration",
  "label_key": "datasets.own_workforce_remuneration.label",
  "kind": "activity",
  "dimensions": [
    { "key": "organization", "label_key": "datasets.dim.organization", "field_type": "string", "group": "own_workforce_remuneration", "i18n_namespace": null },
    { "key": "gender", "label_key": "datasets.dim.gender", "field_type": "string", "group": "own_workforce_remuneration", "i18n_namespace": "customDashboard.pivotTable.values.gender" },
    { "key": "job_category", "label_key": "datasets.dim.job_category", "field_type": "string", "group": "own_workforce_remuneration", "i18n_namespace": null },
    { "key": "age", "label_key": "datasets.dim.age", "field_type": "number", "group": "own_workforce_remuneration", "i18n_namespace": null },
    { "key": "period", "label_key": "datasets.dim.period", "field_type": "date", "group": "own_workforce_remuneration", "i18n_namespace": null }
  ],
  "metrics": [
    { "key": "remuneration_avg", "label_key": "datasets.own_workforce_remuneration.metric.remuneration_avg", "agg": "sum", "decimals": 2, "suffix": null, "row_level": false },
    { "key": "wage_gap", "label_key": "datasets.own_workforce_remuneration.metric.wage_gap", "agg": "sum", "decimals": 2, "suffix": "%", "row_level": false }
  ],
  "default_metrics": ["remuneration_avg"],
  "window_required": true,
  "time_axis_default": "year",
  "data_version": null
}
```

## Common Errors

### 401 Unauthorized

**Cause:** the key is invalid, or it does not belong to the organization in `x-organization-id` — the two are looked up as a pair. A request carrying no credentials at all answers `AUTH_REQUIRED` instead.

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

### 404 Not Found

**Cause:** unknown key, or a custom dataset belonging to another organization.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "detail": "Unknown dataset 'own_workforce_salaries'."
}
```

## Related Endpoints

<CardGroup cols={2}>
  <Card title="List available datasets" icon="list" href="/api-reference/datasets/list">
    Where the key comes from
  </Card>

  <Card title="Query datasets" icon="chart-simple" href="/api-reference/datasets/query">
    Run the aggregation
  </Card>
</CardGroup>
