KPI Formula Calculator
business.kpi_calculate
Overview
You send the formula your business has agreed on and the numbers to put in it; we return the value and, step by step, how it was reached — every operation, its operands and its result — so a figure on a dashboard can be explained rather than defended. The grammar is deliberately small: decimal numbers, your input names, plus, minus, times, divide, brackets, and nine arithmetic functions. Nothing here executes code, no name resolves to anything outside the inputs you send, and there is no exponentiation, comparison or conditional — a KPI is a number, not a decision. Division by zero is your call rather than ours: refuse it, treat it as zero, or return no value at all, because a silent zero has ruined more dashboards than any other default in this area.
KPI definitions live in configuration, in spreadsheets and in three services that disagree, and the usual way to make them configurable is to hand a caller's string to an expression engine. Those engines reach method calls, class loading and reflection by design, so that shortcut is remote code execution with extra steps. What you get here is the configurable formula without the engine: a closed grammar, names that resolve only to the inputs supplied, and a trace so the number can be checked rather than believed.
Reliability
Fully deterministic: BigDecimal at 34 significant digits throughout, then rounded once to the scale and rounding mode you declared — no floating point anywhere, so the same request gives the same last digit on every machine. The parser is an in-house recursive-descent parser over a closed grammar; no expression or scripting library is on any path, which a control test asserts against the pack's own sources. Every bound is checked while parsing rather than while evaluating, so a hostile formula is refused before it is walked — including the magnitude and precision of a number written into the formula, which is bounded when it is read exactly as a supplied input is, so no route into the arithmetic is unbounded. An enum value — roundingMode and onDivideByZero — is stripped of surrounding whitespace and upper-cased before it is matched, so ' half_up ' is HALF_UP; a function name is not folded and must be spelled exactly. Failures: KPI_FORMULA_INVALID reports the character offset and the token class expected, never the text at that offset, because a formula can embed confidential metric names — you will see it for an unbalanced bracket, a dotted name, an assignment, an index, a comparison or an exponentiation. KPI_FORMULA_TOO_LONG means past 1000 characters. KPI_TOKEN_LIMIT_EXCEEDED means past 200 tokens, 64 distinct names or 32 function calls. KPI_DEPTH_EXCEEDED means nesting past 16 levels. KPI_FUNCTION_UNSUPPORTED means a called name is not one of the nine, including a differently-cased spelling of one. KPI_IDENTIFIER_UNKNOWN means the formula refers to a name inputs does not supply — never treated as zero. KPI_ARITY_INVALID means a function got the wrong number of arguments. KPI_INPUT_INVALID means inputs cannot be read, a number written into the formula carries more than 34 significant digits or 12 decimal places, a list is used outside sum or avg, or round's second argument is not a whole number of places. KPI_DIVIDE_BY_ZERO is the REJECT branch of a division by zero. KPI_MAGNITUDE_EXCEEDED means a step, or a number written into the formula, passed 1e30. None is worth retrying unchanged.
Context footprint
The trace grows with the number of operations, not with your data: a typical KPI produces a handful of entries, and a 200-token formula a few hundred at most. Set trace to false when you only want the number, and note that a sum over lists shows its operands, so a 1000-number aggregation is the one case the trace is large. A successful call costs 1 execution credit.
When to use it
When a formula has to be data rather than code, and the figure it produces has to be explainable to whoever asks.
When you need conditions, comparisons or lookups, or when the inputs still have to be gathered from somewhere.
Scope
Non-goals
- Does not execute code, and cannot: there is no scripting or expression engine behind it, only a parser for a closed arithmetic grammar
- Does not read a metric from a database, an API or anywhere else — every number comes from inputs
- Does not offer comparisons, conditionals or booleans: a KPI is a number, not a decision
- Does not offer exponentiation, because one short expression can otherwise cost unbounded work
- Does not remember a formula between calls, and does not name or store KPIs
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response does not contain submitted content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| formula | string | Yes | Bounded arithmetic formula over named inputs, functions, and brackets. |
| inputs | object | Yes | Purpose-bound map of 1 through 200 formula identifiers to a scalar or list; extra keys are the identifier names. |
| scale | integer | No | Published decimal places; omit it for 6. |
| roundingMode | string | No | Published rounding mode; omit it for HALF_UP. |
| onDivideByZero | string | No | What a division by zero means; omit it for REJECT. |
| trace | boolean | No | Whether step-by-step working is returned; omit it for true. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| value | number | Yes | Published result at the declared scale, null only under onDivideByZero NULL. |
| scale | integer | Yes | Decimal places actually applied to published figures. |
| roundingMode | string | Yes | Rounding mode actually applied to published figures. |
| formulaTrace | array | Yes | Bounded array of 0 through 200 evaluation steps in order; empty when trace is false. |
| identifiersUsed | array | Yes | Bounded array of 0 through 200 identifier names in alphabetical order. |
| identifiersUnused | array | Yes | Bounded array of 0 through 200 identifier names in alphabetical order. |
| tokenCount | integer | Yes | How many tokens the formula parsed to. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"business.kpi_calculate","input":{"formula":"(revenue - cost) / revenue","inputs":{"revenue":250000,"cost":180000},"scale":4}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("business.kpi_calculate", {"formula":"round((revenue - cost) / revenue * 100, 2)","inputs":{"revenue":250000,"cost":180000},"scale":2});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| KPI_FORMULA_INVALID | 400 | No | The request payload failed this skill's input validation. |
| KPI_FORMULA_TOO_LONG | 429 | No | A size, quota, rate, or time limit was exceeded. |
| KPI_TOKEN_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| KPI_DEPTH_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| KPI_FUNCTION_UNSUPPORTED | 400 | No | The request payload failed this skill's input validation. |
| KPI_IDENTIFIER_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| KPI_ARITY_INVALID | 400 | No | The request payload failed this skill's input validation. |
| KPI_INPUT_INVALID | 400 | No | The request payload failed this skill's input validation. |
| KPI_DIVIDE_BY_ZERO | 400 | No | The request payload failed this skill's input validation. |
| KPI_MAGNITUDE_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not execute code, and cannot: there is no scripting or expression engine behind it, only a parser for a closed arithmetic grammar
- Does not read a metric from a database, an API or anywhere else — every number comes from inputs
- Does not offer comparisons, conditionals or booleans: a KPI is a number, not a decision
- Does not offer exponentiation, because one short expression can otherwise cost unbounded work
- Does not remember a formula between calls, and does not name or store KPIs
- Anything that needs a condition, a comparison or a lookup — that is not this grammar
- Gathering the inputs: fetch them first and send them
- General expression evaluation, which is what this skill deliberately is not
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "business.kpi_calculate",
"input": {
"formula": "round((revenue - cost) / revenue * 100, 2)",
"inputs": {
"revenue": 250000,
"cost": 180000
},
"scale": 2
}
}Examples
Gross margin as a percentage, with every step of the arithmetic shown
{
"formula": "round((revenue - cost) / revenue * 100, 2)",
"inputs": {
"revenue": 250000,
"cost": 180000
},
"scale": 2
}{
"value": 28,
"formulaTrace": [
{
"step": 1,
"expression": "revenue - cost",
"operands": [
250000,
180000
],
"result": 70000
},
{
"step": 2,
"expression": "(revenue - cost) / revenue",
"operands": [
70000,
250000
],
"result": 0.28
},
{
"step": 3,
"expression": "((revenue - cost) / revenue) * 100",
"operands": [
0.28,
100
],
"result": 28
},
{
"step": 4,
"expression": "round(((revenue - cost) / revenue) * 100, 2)",
"operands": [
28,
2
],
"result": 28
}
],
"identifiersUsed": [
"cost",
"revenue"
],
"tokenCount": 14,
"scale": 2,
"roundingMode": "HALF_UP",
"identifiersUnused": []
}An average over a list of weekly figures, divided by a count that is zero and guarded by safeDiv
{
"formula": "safeDiv(sum(weekly), count, 0)",
"inputs": {
"count": 0,
"weekly": [
10,
20,
30
]
},
"scale": 2
}{
"value": 0,
"formulaTrace": [
{
"step": 1,
"expression": "sum(weekly)",
"operands": [
10,
20,
30
],
"result": 60
},
{
"step": 2,
"expression": "safeDiv(sum(weekly), count, 0)",
"operands": [
60,
0,
0
],
"result": 0
}
],
"identifiersUsed": [
"count",
"weekly"
],
"tokenCount": 11,
"scale": 2,
"roundingMode": "HALF_UP",
"identifiersUnused": []
}Recommended use cases
- Keep one KPI definition in configuration and evaluate it identically everywhere
- Show the arithmetic behind a dashboard figure to whoever queries it
- Let a business user change a KPI formula without a code change, safely
- Aggregate a list of period figures with sum or avg inside a larger formula
Avoid for
- Anything that needs a condition, a comparison or a lookup — that is not this grammar
- Gathering the inputs: fetch them first and send them
- General expression evaluation, which is what this skill deliberately is not
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.