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

# Formulas

> Compute a metric from other metrics with math and comparisons

A formula metric computes its value from other metrics on the same call. Use one to roll several focused metrics into a single score, or to turn them into one pass/fail gate:

```
{{Task Completion >= 2}} AND {{Instruction Follow >= 4}}        →  Boolean "Successful call"
({{Empathy}} + {{Resolution}} + {{Clarity}}) / 3                 →  Numeric "Quality score"
```

Formulas don't call a model: they read values Roark has already recorded, so the same inputs always give the same result, and formulas aren't billed like LLM Judge metrics.

***

## Quickstart

Formulas aren't a [config as code](https://docs.roark.ai/documentation/config-as-code/overview.md) kind yet. Create the formula once with the SDK under a stable `slug`, then collect it from config by that slug. You can also build it in Studio.

### Create it with the SDK

Create the metric with `calculationType: "FORMULA"`. List every metric the formula references in `sources`:

```typescript Node theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
import Roark from '@roarkanalytics/sdk'

const client = new Roark({ bearerToken: process.env.ROARK_API_BEARER_TOKEN })

const { data: definitions } = await client.metric.listDefinitions()
const idOf = (slug: string) => definitions.find((d) => d.metricId === slug)!.id

const taskCompletion = idOf('task_completion')
const instructionFollow = idOf('instruction_follow')

const metric = await client.metric.createDefinition({
  calculationType: 'FORMULA',
  name: 'Successful call',
  slug: 'successful_call',
  outputType: 'BOOLEAN',
  formula: `{{id:${taskCompletion} >= 2}} AND {{id:${instructionFollow} >= 4}}`,
  sources: [{ sourceMetricDefinitionId: taskCompletion }, { sourceMetricDefinitionId: instructionFollow }],
})
```

```python Python theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
import os
from roark_analytics import Roark

client = Roark(bearer_token=os.environ.get("ROARK_API_BEARER_TOKEN"))

definitions = client.metric.list_definitions().data
id_of = lambda slug: next(d.id for d in definitions if d.metric_id == slug)

task_completion = id_of("task_completion")
instruction_follow = id_of("instruction_follow")

metric = client.metric.create_definition(
    calculation_type="FORMULA",
    name="Successful call",
    slug="successful_call",
    output_type="BOOLEAN",
    formula=f"{{{{id:{task_completion} >= 2}}}} AND {{{{id:{instruction_follow} >= 4}}}}",
    sources=[
        {"source_metric_definition_id": task_completion},
        {"source_metric_definition_id": instruction_follow},
    ],
)
```

```bash cURL theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
curl -X POST https://api.roark.ai/v1/metric/definitions \
  -H "Authorization: Bearer $ROARK_API_BEARER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "calculationType": "FORMULA",
    "name": "Successful call",
    "slug": "successful_call",
    "outputType": "BOOLEAN",
    "formula": "{{id:<task_completion id> >= 2}} AND {{id:<instruction_follow id> >= 4}}",
    "sources": [
      { "sourceMetricDefinitionId": "<task_completion id>" },
      { "sourceMetricDefinitionId": "<instruction_follow id>" }
    ]
  }'
```

The response is the metric definition, with the expression and sources echoed back under `formula`.

| Field | Type | Required | Description |
| :- | :- | :- | :- |
| `name` | string | Yes | Name of the metric (1-100 characters) |
| `outputType` | string | Yes | `NUMERIC` or `BOOLEAN` |
| `formula` | string | Yes | The expression, with `{{id:<uuid>}}` references (up to 500 characters) |
| `sources` | object\[] | Yes | At least two: each `sourceMetricDefinitionId`, plus an optional `sourceVariantId` to read a [variant](https://docs.roark.ai/documentation/metrics/studio.md#system-metrics-and-variants) of that metric |
| `slug` | string | No | Stable identifier. Generated from the name if omitted |
| `analysisPackageId` | string | No | Package to add the metric to. Defaults to your project's Custom Metrics package |

To change the expression later, `PUT /v1/metric/definitions/{idOrSlug}` with the new `formula`, and `sources` if they changed. The same request sets `booleanTrueLabel` and `booleanFalseLabel` on a Boolean formula. Each change creates a new metric version.

### Collect it from config

Once the formula exists, list its slug in a [collector](https://docs.roark.ai/documentation/config-as-code/collectors.md) next to your other metrics, and Roark scores it on every matching call. Roark collects the formula's source metrics along with it, so you don't list them:

```typescript roark.config.ts theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
import { defineCollector, defineConfig } from '@roarkanalytics/sdk/lib/config';

const frontdeskQuality = defineCollector({
  name: 'frontdesk-quality',
  modality: 'call',
  status: 'ACTIVE',
  metrics: ['successful_call'],
});

export default defineConfig({
  resources: [frontdeskQuality],
});
```

```yaml theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
# yaml-language-server: $schema=https://roark.ai/roark-config.schema.json
kind: collector
name: frontdesk-quality
modality: call
status: ACTIVE
metrics:
  - successful_call
```

```bash theme={"theme":{"light":"everforest-light","dark":"everforest-dark"}}
roark config apply roark.config.ts
```

Apply fails with `unknown metric "successful_call"` until the formula has been created.

### Build it in Studio

Open [Studio](https://docs.roark.ai/documentation/metrics/studio.md) in Author mode and pick the **Formula** engine. The engine is locked after the first save.

![Studio Author mode with the Formula engine selected: the formula \{\{Task Completion >= 2}} AND \{\{Instruction Follow >= 4}} AND \{\{Call Reason != "Wrong number"}}, a toolbar with Insert metric and the AND, OR, NOT and parenthesis operators, a Boolean badge, the letter map A Task Completion, B Instruction Follow, C Call Reason, the preview \[A\] >= 2 AND \[B\] >= 4 AND \[C\] != "Wrong number", and the True and False labels Successful call and Unsuccessful call](https://mintcdn.com/roark/s80NtcBbMfjE9Dti/images/metrics/formula-metric.png?fit=max\&auto=format\&n=s80NtcBbMfjE9Dti\&q=85\&s=aa2f13dfa70fdfe5c1c71a05e453cfe1)

Type `{{` or click **Insert metric** to pick a source. Studio only offers metrics the formula can use. Click a comparison to change its operator or value. The toolbar shows the operators for the output you're building, and the preview underneath shows the formula with each source as a letter.

***

## Writing a Formula

A formula mixes **metric references** in `{{double braces}}` with operators.

### References

| Reference | Gives |
| :- | :- |
| `{{Empathy}}` | The metric's value on the call, as a number |
| `{{Empathy >= 4}}` | A comparison: `1` when it holds, `0` when it doesn't |

In Studio you reference metrics by name. Studio stores them by id, so renaming a source metric doesn't break the formula. Through the API, write the id: `{{id:<metric definition id>}}` and `{{id:<metric definition id> >= 4}}`.

A plain reference reads Numeric, Count, and Scale values as they are, and a Boolean as `1` or `0`. Compare a value inside the braces with:

| Source type | Compare with | Example |
| :- | :- | :- |
| Numeric, Count, Scale | `==` `!=` `>` `>=` `<` `<=` and a number | `{{Frustration Score <= 2}}` |
| Boolean | `==` `!=` and `TRUE` or `FALSE` | `{{Identity Verified == TRUE}}` |
| Classification | `==` `!=` and a label in quotes, matched without case | `{{Call Reason != "Wrong number"}}` |

Text metrics can't be used in a formula.

### Operators

| Operators | Use |
| :- | :- |
| `+` `-` `*` `/` and `( )` | Arithmetic and grouping |
| `AND` `OR` `NOT` | Combine comparisons |
| `TRUE` `FALSE` | `1` and `0` |
| `MIN(a, b, ...)` `MAX(a, b, ...)` `AVG(a, b, ...)` `ABS(a)` | Functions over references and numbers |

### Output

A formula returns a **Numeric** or a **Boolean**:

* **Numeric** for arithmetic over plain references: `({{Empathy}} + {{Resolution}}) / 2`.
* **Boolean** for comparisons combined with `AND`, `OR`, and `NOT`. Any non-zero result is true.

Studio picks the output from the formula: all plain references make it Numeric, all comparisons make it Boolean. A formula that mixes the two is refused in Studio. For a Boolean, name the two outcomes with the True and False labels.

### Rules

* At least two metric references, up to 500 characters, with balanced parentheses.
* Every source must record a value per call. Metrics that only record values per segment or turn, such as Response Time, can't be referenced. To build on those, use a [pattern](https://docs.roark.ai/documentation/metrics/custom-metrics/patterns.md) or a [threshold](https://docs.roark.ai/documentation/metrics/thresholds.md).
* A formula can reference other formulas and thresholds, as long as they don't loop back to it. Roark computes the sources first.

***

## Reading the Result

Each value carries its working in the reasoning, with every source's value, for example:

```
Formula: {{Task Completion >= 2}} AND {{Instruction Follow >= 4}} → 1 (Task Completion >= 2 = true; Instruction Follow >= 4 = true)
```

### When a source is missing

If any source has no value on the call, the formula records **N/A**, and the reasoning names the sources it couldn't read. It never fills a missing source with zero. Add the sources to the same [collector](https://docs.roark.ai/documentation/metrics/metric-collectors.md) or [run plan](https://docs.roark.ai/documentation/simulation-testing/run-plans.md) as the formula, so they run on the same calls.

A formula that can't produce a finite number, such as a division by a source that is `0`, fails on that call instead of recording a value. Guard divisions with `MAX({{Turns}}, 1)`.

***

## What's Next

> **[LLM Judge](https://docs.roark.ai/documentation/metrics/custom-metrics/llm-judge.md)**
>
> Write the focused metrics a formula combines

> **[Thresholds](https://docs.roark.ai/documentation/metrics/thresholds.md)**
>
> Turn a single metric into a pass/fail check

> **[Patterns](https://docs.roark.ai/documentation/metrics/custom-metrics/patterns.md)**
>
> Detect what follows what within a call

> **[Collectors](https://docs.roark.ai/documentation/metrics/metric-collectors.md)**
>
> Run the formula automatically on live calls


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.