Ledgers and metrics

Read any number in Continual from its ledger, list the records behind it, and define your own metrics through the API.

Every number in Continual is a sum of ledger entries. This page shows how to read a metric, read a ledger directly, list the records behind a number, and define your own metrics. The examples use CONTINUAL_URL and CONTINUAL_API_KEY, as REST API sets up.

Read a metric

List your organization's metrics, then read one over a period:

The answer for Pipeline, the first week of its series shown:

JSON
{
  "currency": "USD",
  "value": "1203601.600000",
  "compared": "762658.800000",
  "sum": "1203601.600000",
  "per": null,
  "series": [
    {
      "startDate": "2026-09-28",
      "value": "837062.400000",
      "compared": "0",
      "sum": "837062.400000",
      "per": null
    }
  ],
  "groups": []
}
FieldWhat it holds
currencyThe currency money is in: your organization's, unless you name another; null otherwise
valueThe value for the period: a flow's total over it, or a balance at its end
comparedThe value of what you compared it with
sumWhat the value is made of: the sum, which a ratio divides
perWhat a ratio's sum is divided by; null for a plain sum
seriesOne value per day, week, or month, each with its compared, sum, and per
groupsOne value per group, when you name groupBy

A sum of nothing is "0"; a ratio over nothing is null. Numbers are decimal strings, so they keep every digit.

What a read takes

metrics.values and a ledger's balances take the same inputs.

InputWhat it says
fromDateThe period's first day
toDateThe period's last day
scenarioactual unless named
asKnownAtOnly what was recorded by this moment, to read a number as it was reported then
whereWhich entries, in the filter language, as JSON
groupByWhat to group by, as a JSON array

A metric's values also take grain (day, week, or month), compareTo (previous, lastYear, or a scenario such as budget), and currency.

where and groupBy name a ledger's dimensions, such as stage or owner, or a field of the record a dimension names, such as customer.name or account.type. A record's field is read as the record is now. groupBy also takes book, currency, unit, actor, record, day, week, and month. A flow can be grouped by actor; a balance cannot.

Pipeline by customer name:

Shell
curl -G "$CONTINUAL_URL/api/v1/metrics/<metric ID>:values" \
  -H "Authorization: Bearer $CONTINUAL_API_KEY" \
  --data-urlencode 'fromDate=2026-10-01' \
  --data-urlencode 'toDate=2026-10-31' \
  --data-urlencode 'groupBy=["customer.name"]'

List the records behind a number

Group by record to list the records a value is made of, each with its share. The deals won in October, by Bookings:

Shell
curl -G "$CONTINUAL_URL/api/v1/metrics/<metric ID>:values" \
  -H "Authorization: Bearer $CONTINUAL_API_KEY" \
  --data-urlencode 'fromDate=2026-10-01' \
  --data-urlencode 'toDate=2026-10-31' \
  --data-urlencode 'groupBy=["record"]'

Each group's record is a record ID. Read the records with records.batchGet (POST /api/v1/records:batchGet).

Read a ledger

Each ledger has two Queries at its name: balances, which sums its entries for each group with every measure, and entries, which lists the entries of one record.

LedgerWhat it keeps
finance.generalLedgerWhat invoices, payments, and journal entries post
sales.dealLifecycleDeals moving between stages, with their amounts
projects.taskLifecycleTasks moving between statuses
<record type>LifecycleEvery record type with a status, such as todoLifecycle

Deals by stage at the end of October:

Shell
curl -G "$CONTINUAL_URL/api/v1/sales.dealLifecycle:balances" \
  -H "Authorization: Bearer $CONTINUAL_API_KEY" \
  --data-urlencode 'toDate=2026-10-31' \
  --data-urlencode 'groupBy=["stage"]'
JSON
{
  "rows": [
    {
      "group": { "stage": "lost" },
      "bookAmount": "0",
      "amount": "45600.00",
      "quantity": null,
      "measures": {
        "count": "3",
        "arrivals": "3",
        "entered": "5372890200.000"
      },
      "entries": 3
    }
  ]
}

A lifecycle ledger's measures:

MeasureWhat it is
countHow many records are in each state
arrivalsHow many entered a state
departuresHow many left a state
durationThe seconds each spent in a state, posted as it leaves
enteredThe sum of the times, in seconds since 1970, that the records in a state entered it

A metric reads age, how long the records in a state have been there, from count and entered.

The entries of one record, such as a deal:

Shell
curl -G "$CONTINUAL_URL/api/v1/sales.dealLifecycle:entries" \
  -H "Authorization: Bearer $CONTINUAL_API_KEY" \
  --data-urlencode 'record=<deal ID>'

Each entry has its accountingDate, its effectiveAt and recordedAt times, its actor, its dimensions, and its amounts and measures. Entries are only ever added: a correction posts an entry that reverses another, named in its corrects.

Define your own metric

A metric is a record. Create one with metrics.create. This one counts the deals lost in a period:

Shell
curl -X POST "$CONTINUAL_URL/api/v1/metrics" \
  -H "Authorization: Bearer $CONTINUAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Deals lost",
    "unit": "count",
    "direction": "lower",
    "sum": {
      "kind": "flow",
      "ledger": "sales.dealLifecycle",
      "measure": "arrivals",
      "where": { "field": "stage", "operator": "eq", "value": "lost" }
    },
    "requestId": "create-deals-lost"
  }'
FieldWhat it says
sumWhat it adds up: a ledger, a measure, and which entries
perWhat the sum is divided by, for a ratio; leave it out for a plain sum
unitcount, money, percent (the ratio times 100), or duration (seconds)
directionhigher or lower: which way is good

A sum's kind is flow, for the entries dated in a period, or balance, for every entry through its end. Its measure is amount, bookAmount, quantity, or one of the ledger's measures. Set normal to credit to read credits as positive, as revenue and liabilities are. A new metric reads its ledger's whole history at once.

Revenue, from every account whose type is revenue in the general ledger:

JSON
{
  "kind": "flow",
  "ledger": "finance.generalLedger",
  "measure": "bookAmount",
  "where": { "field": "account.type", "operator": "eq", "value": "revenue" },
  "normal": "credit"
}

The metrics your apps ship are records too, with a key such as sales.winRate. Installing a new version of an app updates them.