Skip to content

Metric scores

Two endpoints, meant to be used together. You list the metrics in your environment to find the one you want and learn what its numbers mean, then you ask for what every agent scored on it between two dates.

That pairing is the intended flow — the ids you pass to metric-scores come from metrics, and so does the answer to "is this figure in euros or in cents".

List metrics

GET /integrations/api/v1/metrics

No parameters. You get every metric in your environment, in the same order the Metrics page in the admin shows them.

json
{
  "metrics": [
    {
      "id": 1,
      "name": "Sales",
      "unit": null,
      "value_unit": "raw",
      "display_mode": "number",
      "lower_is_better": false,
      "can_rank": true
    },
    {
      "id": 2,
      "name": "Contract value",
      "unit": "euro",
      "value_unit": "euros",
      "display_mode": "currency",
      "lower_is_better": false,
      "can_rank": true
    }
  ]
}
FieldMeaning
idThe metric_id you pass to metric-scores and to the goal endpoints.
nameThe metric's name as it appears throughout SalesDash.
unitThe label a metric is shown with in the interface — uur, euro, sales. Free text, chosen in the admin, and null if nobody set one. Do not do arithmetic based on this.
value_unitThe unit the value numbers are actually in. This is the one to trust — see below.
display_modeHow SalesDash formats the metric: number, percentage, currency, duration, score, ratio or raw. null means nobody has chosen one yet.
lower_is_bettertrue when a smaller number is a better result — average handling time, cost per lead.
can_rankWhether the metric can be scored per agent. false means it only exists at organisation level, and metric-scores will refuse it.

unit is a label, value_unit is the unit

These two disagree more often than you would expect, and it is deliberate. unit is the word your colleagues chose to see next to the figure; value_unit is what the raw number in the API is measured in.

value_unitThe value you get back
eurosEuros, already converted — 181942 is €181.942,00.
secondsSeconds, even when the metric is labelled uur. Divide by 3600 yourself for hours.
fractionA fraction, not a percentage — a 42% conversion rate arrives as 0.42.
rawA plain count or score, in no particular unit.
nullSalesDash is not claiming a unit. Most ratio metrics are null, because a ratio's unit comes from the two metrics it divides rather than from anything on the metric itself — a ratio formatted as a duration (average handling time, say) is the exception and reports seconds. For a null, read value_formatted instead of doing arithmetic, or ask whoever configured the ratio.

Every response that carries a number also carries its value_unit, so you never have to remember which metric was which.

Metrics that could not be read

Occasionally a metric's configuration is broken — a ratio whose underlying metric was deleted, or a metric nobody finished setting up. Listing everything else is more useful than failing outright, so such a metric is still listed, marked:

json
{
  "id": 14,
  "name": "Cost per booked demo",
  "unit": null,
  "value_unit": null,
  "display_mode": null,
  "lower_is_better": false,
  "can_rank": null,
  "unavailable": true
}

can_rank: null is not the same as false. false says this metric is organisation-level; null says nobody could read this metric, so nobody knows. Skip a metric with unavailable, and fix it in the admin — asking for its scores gives you a 422.

Two things have to be true before a metric can be scored

can_rank must be true and display_mode must not be null. They are separate because they are separate problems: one is a metric that was never meant to be ranked per person, the other is a metric that just needs a display mode set. Check both before you call metric-scores.

Read the scores

GET /integrations/api/v1/metric-scores?metric_id=2&start_date=2026-08-01&end_date=2026-08-25
ParameterRequiredNotes
metric_idyesFrom the list above.
start_dateyesYYYY-MM-DD.
end_dateyesYYYY-MM-DD, on or after start_date.

Both dates are inclusive — the example above covers all of 25 August. The format is strict on purpose: a loose 2026 would otherwise be read as a time on today's date and you would get a one-minute window back without being told.

json
{
  "metric": {
    "id": 2,
    "name": "Contract value",
    "unit": "euro",
    "value_unit": "euros",
    "display_mode": "currency",
    "lower_is_better": false
  },
  "date_range": {
    "start": "2026-08-01",
    "end": "2026-08-25"
  },
  "scores": [
    {
      "rank": 1,
      "agent_id": 22,
      "name": "Tamara Wilson",
      "team_id": 2,
      "value": 181942,
      "value_formatted": "€181.942,00"
    },
    {
      "rank": 2,
      "agent_id": 7,
      "name": "Lily Davis",
      "team_id": 2,
      "value": 104930,
      "value_formatted": "€104.930,00"
    }
  ]
}

value is the number to calculate with, in value_unit. value_formatted is the same number written the way SalesDash writes it on a dashboard — use it when you are putting the figure straight in front of a person, and you will match what they see elsewhere in SalesDash.

What's in the list, and what isn't

Every currently-active agent, and only those. Someone who left is not in the list even if they sold something inside the window. Everyone still active is, including those who scored nothing — they come back with 0. There is no page size and no top-N: you asked for the metric's scores, so you get the whole team.

Ranks are consecutive. Two agents on the same value get two different ranks, in no meaningful order between them. If ties matter to you, group on value yourself.

A zero can mean "nothing happened", and on a lower-is-better metric that wins

An active agent with no activity in the window scores 0. On a metric where lower is better — average handling time, cost per lead — that 0 takes rank 1, ahead of everyone who did the work.

This is how ranking behaves everywhere in SalesDash, dashboards included, so the API is not doing anything unusual here. But if you are publishing a lower-is-better ranking somewhere people will read it, drop or flag the zero-value rows yourself first.

Refusals

A 422 on this endpoint is almost always about the metric rather than the request:

  • The metric is organisation-level (can_rank: false) — there is no per-agent number to give you.
  • The metric has no display mode — SalesDash cannot format its scores. Set one on the metric in the admin.
  • The metric's configuration cannot be read — the same rows the list marks unavailable.

Filtering your list on can_rank: true and a non-null display_mode avoids all three.