---
title: "Report project metrics"
method: POST
path: "/api/v1/projects/{projectId}/metrics"
tags: ["Projects"]
---

# Report project metrics

`POST /api/v1/projects/{projectId}/metrics`

## Request

- Base URL: `https://app.khotan.com`
- URL: `https://app.khotan.com/api/v1/projects/{projectId}/metrics`
- Auth: one of:
  - HTTP bearer
  - API key in header `x-api-key`

Creates or updates metric definitions and sets their points. Idempotent: series and breakdown points are keyed by bucket (and category), scatter points by id, so a retried report never double-counts. The whole report is validated before anything is written. Curation made in Khotan — name, order, hidden, headline — is never overwritten.

## Path parameters

- `projectId` string, required

## Request body

- object
  - `metrics` union[], required: At most 50 metrics and 10000 points in total per report.
    - union
      - object
        - `key` string, required
        - `name` string, nullable: Default display name. Ignored for display once an author renames the metric in Khotan.
        - `description` string, nullable
        - `unit` string, required: `count`, `percent` (percentage points, so 45.2 means 45.2%), `duration` (seconds), `currency:<ISO 4217 code>`, or a custom label of at most 16 characters.
        - `goal` 'increase' | 'decrease' | 'neutral', required: Which direction of change is good news.
        - `aggregate` 'sum' | 'average' | 'latest', required: How values across a range combine into one number.
        - `shape` 'series', required
        - `grain` 'day' | 'hour', required
        - `points` object[]
          - `bucket` string, required: `YYYY-MM-DD` (the reporting app's own business date) for day grain; an ISO 8601 timestamp with an offset for hour grain, truncated to the UTC hour.
          - `value` number, required: A finite number.
      - object
        - `key` string, required
        - `name` string, nullable: Default display name. Ignored for display once an author renames the metric in Khotan.
        - `description` string, nullable
        - `unit` string, required: `count`, `percent` (percentage points, so 45.2 means 45.2%), `duration` (seconds), `currency:<ISO 4217 code>`, or a custom label of at most 16 characters.
        - `goal` 'increase' | 'decrease' | 'neutral', required: Which direction of change is good news.
        - `aggregate` 'sum' | 'average' | 'latest', required: How values across a range combine into one number.
        - `shape` 'breakdown', required
        - `grain` 'day' | 'hour', required
        - `points` object[]
          - `bucket` string, required: `YYYY-MM-DD` (the reporting app's own business date) for day grain; an ISO 8601 timestamp with an offset for hour grain, truncated to the UTC hour.
          - `category` string, required
          - `value` number, required: A finite number.
      - object
        - `key` string, required
        - `name` string, nullable: Default display name. Ignored for display once an author renames the metric in Khotan.
        - `description` string, nullable
        - `unit` string, required: `count`, `percent` (percentage points, so 45.2 means 45.2%), `duration` (seconds), `currency:<ISO 4217 code>`, or a custom label of at most 16 characters.
        - `goal` 'increase' | 'decrease' | 'neutral', required: Which direction of change is good news.
        - `aggregate` 'sum' | 'average' | 'latest', required: How values across a range combine into one number.
        - `shape` 'scatter', required
        - `grain` 'null', nullable
        - `xLabel` string, required
        - `xUnit` string, required: `count`, `percent` (percentage points, so 45.2 means 45.2%), `duration` (seconds), `currency:<ISO 4217 code>`, or a custom label of at most 16 characters.
        - `yLabel` string, required
        - `yUnit` string, required: `count`, `percent` (percentage points, so 45.2 means 45.2%), `duration` (seconds), `currency:<ISO 4217 code>`, or a custom label of at most 16 characters.
        - `points` object[]
          - `id` string, required: Client-supplied identity. Reporting an existing id replaces that point.
          - `x` number, required: A finite number.
          - `y` number, required: A finite number.
          - `label` string, nullable
          - `observedAt` string, date-time, nullable

## Response `200`

The metrics and point counts written.

- object
  - `metrics` object[], required
    - `key` string, required
    - `created` boolean, required
    - `pointsWritten` integer, required
  - `metricsWritten` integer, required
  - `pointsWritten` integer, required

## Other responses

- `401`: Authentication is missing or invalid.
- `403`: The caller is not permitted to perform this action.
- `404`: The resource was not found.
- `409`: A metric exists with a different shape or grain, or the project's metric limit would be exceeded.
- `422`: The report is invalid. `details.metricKey` and `details.pointIndex` identify the offending metric and point.
- `429`: Too many requests; retry after the indicated delay.

---

[API](https://skmtc.dev/khotan/apis/khotan-api.md) - [All operations](https://skmtc.dev/khotan/apis/khotan-api/llms.txt) - [OpenAPI document](https://skmtc.dev/khotan/apis/khotan-api/revisions/90b512a53e42?raw)
