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

# Upload key metrics

> Manage the key-metric values of an internal project programmatically instead of uploading CSVs.

Key metrics belong to an [internal project](/internal-projects). On a revenue-linked project, use the [Revenue API](/api/revenue) instead.

<CodeGroup>
  ```python Python theme={null}
  import weflayr

  client = weflayr.client(api_key="wf-...")
  ```

  ```js Node.js theme={null}
  const weflayr = require('weflayr');

  const client = weflayr.client({ apiKey: 'wf-...' });
  ```

  ```bash cURL theme={null}
  # Passed as a Bearer token on every request
  export WEFLAYR_API_KEY="wf-..."
  ```
</CodeGroup>

## Set key metrics

A user can have several lines of the same metric in the same month (they add up):

<CodeGroup>
  ```python Python theme={null}
  from weflayr.openapi.models import SetKeyMetricsInput

  client.set_key_metrics(body=SetKeyMetricsInput.from_dict({
      "rows": [
          {"user_name": "u_123", "metric_name": "Meetings booked", "amount": 3, "month": "2026-06"},
          {"user_name": "u_456", "metric_name": "Meetings booked", "amount": 7, "month": "2026-06"},
      ]
  }))
  ```

  ```js Node.js theme={null}
  await weflayr.api.setKeyMetrics({
    client,
    throwOnError: true,
    body: {
      rows: [
        { userName: 'u_123', metricName: 'Meetings booked', amount: 3, month: '2026-06' },
        { userName: 'u_456', metricName: 'Meetings booked', amount: 7, month: '2026-06' },
      ],
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://app.weflayr.com/api/key-metrics/ \
    -H "Authorization: Bearer $WEFLAYR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "rows": [
        {"user_name": "u_123", "metric_name": "Meetings booked", "amount": 3, "month": "2026-06"},
        {"user_name": "u_456", "metric_name": "Meetings booked", "amount": 7, "month": "2026-06"}
      ]
    }'
  ```
</CodeGroup>

**Output:**

```json theme={null}
{"ok": true, "rows_inserted": 2}
```

**Input params:**

<ParamField body="rows" type="array of row objects" required>
  The key-metric lines to book, described below. Up to 1,000 per call.
</ParamField>

<ParamField body="upsert" type="boolean" default="false">
  If a row's `metric_id` already exists (c.f. `rows` parameters below), overwrite it instead of throwing an error.
</ParamField>

Each object inside `rows` accepts:

<ParamField body="user_name" type="string" required>
  The user the value belongs to.
</ParamField>

<ParamField body="metric_name" type="string" required>
  Name of the key metric. The first time you post a new one, it's automatically added to the project's key metrics.
</ParamField>

<ParamField body="amount" type="number" required>
  The value to book.
</ParamField>

<ParamField body="month" type="&#x22;YYYY-MM&#x22;">
  The calendar month the value belongs to. Required unless you set `month_start` and `month_end` instead.
</ParamField>

<ParamField body="month_start, month_end" type="&#x22;YYYY-MM&#x22;">
  Bill over a multi-month period (e.g. quarterly) instead of a single `month`. `amount` is split evenly over the period. Set either `month`, or both `month_start` and `month_end`, never both forms on the same row.
</ParamField>

<ParamField body="metric_id" type="string">
  An id for the key-metric row over which uniqueness is checked, typically from your own tracking system.
</ParamField>

<Note>
  API values are their own key-metric source, alongside CSV uploads. Sources add up: if you post a value for a user-month that also has a CSV upload, Weflayr assumes it is the sum. We recommend using one source only to avoid double counting.
</Note>

## Get key metrics

<CodeGroup>
  ```python Python theme={null}
  client.get_key_metrics(user_names=["u_123", "u_456"])
  ```

  ```js Node.js theme={null}
  await weflayr.api.getKeyMetrics({
    client,
    throwOnError: true,
    query: { userNames: ['u_123', 'u_456'] },
  });
  ```

  ```bash cURL theme={null}
  curl "https://app.weflayr.com/api/key-metrics/?user_names=u_123&user_names=u_456" \
    -H "Authorization: Bearer $WEFLAYR_API_KEY"
  ```
</CodeGroup>

**Output:**

```json theme={null}
{
  "rows": [
    {"metric_id": "b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41", "user_name": "u_123", "metric_name": "Meetings booked", "month": "2026-06", "amount": 3.0, "source": "api"}
  ],
  "next_cursor": null,
  "has_more": false
}
```

**Input params:** passed in the query string.

<ParamField query="user_names" type="list[string]">
  Specify the names of the users whose key metrics should be fetched. Not setting it means "all users".
</ParamField>

<ParamField query="limit" type="integer" default="100">
  Numbers of rows to fetch. Max `1000`.
</ParamField>

<ParamField query="cursor" type="string">
  The route is paginated: pass the previous page's `next_cursor` to fetch the next page. PS: `has_more` from the output says whether another page exists.
</ParamField>

## Delete key metrics

<CodeGroup>
  ```python Python theme={null}
  client.delete_key_metrics(user_names=["u_123", "u_456"])
  ```

  ```js Node.js theme={null}
  await weflayr.api.deleteKeyMetrics({
    client,
    throwOnError: true,
    query: { userNames: ['u_123', 'u_456'] },
  });
  ```

  ```bash cURL theme={null}
  curl -X DELETE "https://app.weflayr.com/api/key-metrics/?user_names=u_123&user_names=u_456" \
    -H "Authorization: Bearer $WEFLAYR_API_KEY"
  ```
</CodeGroup>

**Output:**

```json theme={null}
{"ok": true, "rows_deleted": 2}
```

**Input params:** passed in the query string.

<ParamField query="user_names" type="list[string]" required>
  Delete all these users' API-booked key metrics.
</ParamField>

<ParamField query="month_start, month_end" type="&#x22;YYYY-MM&#x22;">
  Narrow the delete to this inclusive month range. Omit both to delete every API-booked value of the named users.
</ParamField>

PS: CSV uploads are managed by their own source and cannot be deleted via this route
