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

# Metrics Configuration Reference

> Complete reference for configuring billable metrics

This page provides complete reference documentation for configuring billable metrics in Alguna. Use this as a reference when setting up metrics in the dashboard or via API.

***

## Metric Structure

A billable metric defines how usage events are aggregated for billing. Here's the complete structure:

```json theme={null}
{
  "id": "mtr_01H1VECT",
  "name": "API Calls",
  "description": "Number of API requests made",
  "event_name": "api_call",
  "aggregation": {
    "method": "count"
  },
  "filter_groups": [],
  "tag_ids": []
}
```

| Field           | Type   | Required | Description                                  |
| --------------- | ------ | -------- | -------------------------------------------- |
| `name`          | string | Yes      | Display name                                 |
| `event_name`    | string | Yes      | Which events to aggregate                    |
| `aggregation`   | object | Yes      | How to aggregate events                      |
| `description`   | string | No       | Human-readable description                   |
| `filter_groups` | array  | No       | Filter events before aggregation             |
| `tag_ids`       | array  | No       | Tag identifiers to associate with the metric |

Metric ids are prefixed `mtr_`. The `id`, `created_at` and `updated_at` fields are read-only and returned by the API — you never send them.

### Create a Metric

```bash theme={null}
curl -X POST https://api.alguna.io/metrics \
  -H "Authorization: Bearer $ALGUNA_API_KEY" \
  -H "Alguna-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "API Calls",
    "event_name": "api_call",
    "aggregation": {
      "method": "count"
    }
  }'
```

Metrics can be created and read through the API (`POST /metrics`, `GET /metrics`, `GET /metrics/{id}`). Editing an existing metric is done in the dashboard.

***

## Aggregation Types

Aggregation defines how event values are combined into a billable quantity. The `method` selects the calculation; `field` names the event property it reads and is required for every method except `count`.

### Count

Count the number of events.

```json theme={null}
{
  "aggregation": {
    "method": "count"
  }
}
```

**Use cases:**

* API calls
* Transactions processed
* Messages sent
* User logins
* Webhook deliveries

**How it works:**

* 3 events received → Result: 3

***

### Count Unique

Count unique values of a specified field.

```json theme={null}
{
  "aggregation": {
    "method": "count_unique",
    "field": "user_id"
  }
}
```

**Use cases:**

* Monthly active users
* Unique IP addresses
* Distinct customers served
* Unique devices

**How it works:**

* Events with user\_id: "u1", "u2", "u1", "u3" → Result: 3 (unique: u1, u2, u3)

***

### Sum

Sum the values of a numeric field.

```json theme={null}
{
  "aggregation": {
    "method": "sum",
    "field": "bytes_transferred"
  }
}
```

**Use cases:**

* Data transfer (bytes/GB)
* Storage used
* Compute hours consumed
* Revenue processed
* Tokens/credits consumed

**How it works:**

* Events with bytes: 1000, 2500, 500 → Result: 4000

***

### Average

Calculate the average value of a numeric field.

```json theme={null}
{
  "aggregation": {
    "method": "average",
    "field": "response_time_ms"
  }
}
```

**Use cases:**

* Average response time
* Average order value
* Average session duration
* Mean compute time

**How it works:**

* Events with values: 100, 150, 200 → Result: 150

***

### Min

Find the minimum value of a numeric field.

```json theme={null}
{
  "aggregation": {
    "method": "min",
    "field": "latency_ms"
  }
}
```

**Use cases:**

* Minimum latency achieved
* Lowest price point
* Fastest response time

**How it works:**

* Events with values: 100, 50, 200 → Result: 50

***

### Max

Find the maximum value of a numeric field.

```json theme={null}
{
  "aggregation": {
    "method": "max",
    "field": "concurrent_users"
  }
}
```

**Use cases:**

* Peak concurrent users
* Maximum storage used during period
* Highest bandwidth reached
* Peak compute instances

**How it works:**

* Events with values: 100, 250, 180 → Result: 250

***

## Filter Operators

Filters allow you to include only specific events in the aggregation. A filter is `{field, operator, value}`, and `value` is always a **string** — even for numeric comparisons.

### String Operators

| Operator       | Description                                          | Example                         |
| -------------- | ---------------------------------------------------- | ------------------------------- |
| `equal`        | Exact match                                          | `region` equal `us-east-1`      |
| `not_equal`    | Does not match                                       | `status` not equal `failed`     |
| `contains`     | Contains substring                                   | `endpoint` contains `/api/v2`   |
| `not_contains` | Does not contain                                     | `path` not contains `/internal` |
| `includes`     | Matches any value in a comma-separated list          | `region` includes `NA,EMEA`     |
| `not_includes` | Matches none of the values in a comma-separated list | `region` not includes `NA,EMEA` |

### Numeric Operators

The event property and the filter value are compared as numbers when both parse as numbers.

| Operator | Description           | Example              |
| -------- | --------------------- | -------------------- |
| `gt`     | Greater than          | `file_size_mb > 100` |
| `gte`    | Greater than or equal | `priority >= 5`      |
| `lt`     | Less than             | `latency_ms < 1000`  |
| `lte`    | Less than or equal    | `retries <= 3`       |

<Note>
  If an event does not carry the filtered property at all, only `not_equal` matches it. Every other operator treats the event as non-matching.
</Note>

### Filter Examples

**Filter by region:**

```json theme={null}
{
  "field": "region",
  "operator": "equal",
  "value": "us-east-1"
}
```

**Exclude failed requests:**

```json theme={null}
{
  "field": "status",
  "operator": "not_equal",
  "value": "failed"
}
```

**Filter several regions at once:**

```json theme={null}
{
  "field": "region",
  "operator": "includes",
  "value": "us-east-1,us-west-2"
}
```

**Filter large files:**

```json theme={null}
{
  "field": "file_size_mb",
  "operator": "gt",
  "value": "100"
}
```

***

## Filter Combinations

Filters live in `filter_groups`. Each group has an `operator` of `and` or `or` that combines the filters inside it. **Groups themselves are always combined with AND** — an event must match every group to be aggregated.

### AND Logic (All Conditions Must Match)

```json theme={null}
{
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "region", "operator": "equal", "value": "us-east-1"},
        {"field": "status", "operator": "equal", "value": "success"}
      ]
    }
  ]
}
```

### OR Logic (Any Condition Can Match)

```json theme={null}
{
  "filter_groups": [
    {
      "operator": "or",
      "filters": [
        {"field": "region", "operator": "equal", "value": "us-east-1"},
        {"field": "region", "operator": "equal", "value": "us-west-2"}
      ]
    }
  ]
}
```

### Combining Groups

Use several groups when each condition must hold independently:

```json theme={null}
{
  "filter_groups": [
    {
      "operator": "or",
      "filters": [
        {"field": "tier", "operator": "equal", "value": "premium"},
        {"field": "tier", "operator": "equal", "value": "enterprise"}
      ]
    },
    {
      "operator": "and",
      "filters": [
        {"field": "status", "operator": "not_equal", "value": "failed"}
      ]
    }
  ]
}
```

This matches: (premium OR enterprise) AND not failed.

***

## Complete Metric Examples

### API Call Counter

Count API calls, excluding errors:

```json theme={null}
{
  "name": "API Calls",
  "description": "Successful API requests",
  "event_name": "api_request",
  "aggregation": {
    "method": "count"
  },
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "status", "operator": "not_equal", "value": "error"}
      ]
    }
  ]
}
```

### Data Transfer

Sum bytes transferred:

```json theme={null}
{
  "name": "Data Transfer",
  "description": "Total data transferred",
  "event_name": "data_transfer",
  "aggregation": {
    "method": "sum",
    "field": "bytes"
  }
}
```

<Warning>
  Alguna aggregates the raw property value and does not convert units. If you price per GB, send the property already expressed in GB rather than in bytes.
</Warning>

### Monthly Active Users

Count unique users:

```json theme={null}
{
  "name": "Monthly Active Users",
  "description": "Unique users who performed an action",
  "event_name": "user_action",
  "aggregation": {
    "method": "count_unique",
    "field": "user_id"
  }
}
```

### Peak Concurrent Users

Track maximum concurrent users:

```json theme={null}
{
  "name": "Peak Concurrent Users",
  "description": "Maximum concurrent users at any point",
  "event_name": "session_snapshot",
  "aggregation": {
    "method": "max",
    "field": "concurrent_count"
  }
}
```

### Compute Hours

Sum compute time in hours:

```json theme={null}
{
  "name": "Compute Hours",
  "description": "Total GPU compute time",
  "event_name": "compute_completed",
  "aggregation": {
    "method": "sum",
    "field": "duration_hours"
  },
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "instance_type", "operator": "equal", "value": "gpu"}
      ]
    }
  ]
}
```

### Storage by Tier

Track storage separately for different tiers:

**Standard Storage:**

```json theme={null}
{
  "name": "Standard Storage (GB)",
  "event_name": "storage_snapshot",
  "aggregation": {
    "method": "max",
    "field": "gigabytes_used"
  },
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "storage_class", "operator": "equal", "value": "standard"}
      ]
    }
  ]
}
```

**Premium Storage:**

```json theme={null}
{
  "name": "Premium Storage (GB)",
  "event_name": "storage_snapshot",
  "aggregation": {
    "method": "max",
    "field": "gigabytes_used"
  },
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "storage_class", "operator": "equal", "value": "premium"}
      ]
    }
  ]
}
```

### API Calls by Region

Create separate metrics for different regions:

**NA Region:**

```json theme={null}
{
  "name": "API Calls (NA)",
  "event_name": "api_request",
  "aggregation": {"method": "count"},
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "region", "operator": "equal", "value": "NA"}
      ]
    }
  ]
}
```

**EMEA Region:**

```json theme={null}
{
  "name": "API Calls (EMEA)",
  "event_name": "api_request",
  "aggregation": {"method": "count"},
  "filter_groups": [
    {
      "operator": "and",
      "filters": [
        {"field": "region", "operator": "equal", "value": "EMEA"}
      ]
    }
  ]
}
```

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Descriptive Names" icon="tag">
    Choose names that clearly identify what's being measured: "API Calls (Premium)" not "Metric 1".
  </Card>

  <Card title="Filter Early" icon="filter">
    Add filters to exclude irrelevant events at the metric level, not in pricing.
  </Card>

  <Card title="Test with Real Data" icon="flask">
    Verify metric calculations with known event data before going live.
  </Card>

  <Card title="Document Definitions" icon="file-lines">
    Use descriptions to explain exactly what each metric measures.
  </Card>
</CardGroup>

***

## Troubleshooting

### Metric Shows Zero

1. Verify events are being sent with the correct `event_name`
2. Check that filters aren't excluding all events
3. Ensure the aggregation field exists in events (for sum, max, etc.)

### Unexpected Values

1. Review filter conditions for typos
2. Confirm the aggregation `method` and `field` are the ones you meant
3. Verify event data types (string vs number)

### Missing Events

1. Confirm events are being sent successfully
2. Check event timestamps are within the billing period
3. Verify the event `account` resolves to the customer on the subscription

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Define Metrics" icon="ruler" href="/docs/billable-metrics/define-metrics">
    Create billable metrics in the dashboard.
  </Card>

  <Card title="Send Usage" icon="paper-plane" href="/docs/billable-metrics/send-usage">
    Send usage events to Alguna.
  </Card>
</CardGroup>
