Skip to content

Numbers over several of your projects at once

GET
/v1/analytics
curl --request GET \
--url https://beta-api.plune.ai/v1/analytics \
--header 'Authorization: Bearer <token>'

The Analytics page in one request (#876, ADR 0039): the cards, the latest runs, the results by priority, a row per project and the values for the filters. The one route that reads more than one project: a session reads the projects projects names — any role, a reader too — or, without it, every project you are a member of; a bearer token reads its own project only (the one it was minted in; a token from before projects — X-Plune-Project, else your default), and naming any other one is a 404. Only numbers and one row per project come back — never runs, cases or results from several projects.

Definitions — the same on the page and in the docs. A run of the period: its startedAt within [from, to], not in the bin, and passing labels, environments and executedBy. The results of the period: the results of those runs; tags and suite act through the case of a result. Executed by X: a result X marked, or one nobody marked in a run X launched — a run passes when one of its results does. Pass rate: passed ÷ every result × 100, skipped included. Cases: live cases now — the date does not act on them. Coverage: automated ÷ (manual + automated). Latest: the last finished run of each project in the period. Flaky: cases scored flaky or critical over their last 50 results, whatever the period. Between parameters AND, within one OR — as on the cases list.

Sends a Server-Timing header (W3C): where the server spent the time, in milliseconds to a tenth — DevTools lists it under Network → Timing. auth is the token or session and the set of projects, counted from the start of the request; projects, flaky, priority and facets are the four reads of the database, each from the moment it is sent until its rows are back — they run side by side, so they do not add up; total is the whole answer, before it is sent. The names are fixed and carry nothing of yours.

from
string format: date-time

The start of the period, inclusive — ISO 8601 with a time zone (2026-08-30T00:00:00+03:00): the browser turns a preset like «Last 30 days» into bounds in its own zone. Absent — no start.

to
string format: date-time

The end of the period, inclusive, as from. Absent — no end.

projects
Array<string>
<= 100 items

Project ids, comma-separated (the key repeated works too) — ids from GET /v1/projects. Absent — every project you are a member of, but none in the bin (ADR 0038). At most 100 either way. One you are not in, or one in the bin, is a 404, the same as an id nobody has.

labels
Array<string>

Run labels. Repeat for any-of.

tags
Array<string>

Case tags, verbatim and without @. Repeat for any-of.

environments
Array<string>

Run environments. Repeat for any-of.

executedBy
Array<string>

People, by userId — facets.people lists them — comma-separated or repeated.

suite
string

A suite, or a folder with everything beneath it — only with exactly one project in the request, as a folder belongs to one.

The page

Media type application/json
object
range
required

The period the numbers are for — the bounds of the request as UTC instants; null — no bound.

object
from
required
string format: date-time
nullable
to
required
string format: date-time
nullable
kpis
required

The cards: the rows of projects added up — counters summed, never rates averaged.

object
passRate
required

Passed ÷ every result of the period × 100 — skipped included; null — no results.

number
nullable
cases
required

Live cases now — the date does not act on them.

integer
runs
required

Runs of the period.

integer
manual
required
integer
automated
required
integer
eval
required
integer
coverage
required

Automated ÷ (manual + automated) × 100 — an eval does not count, it replaces no manual test; null — neither kind.

number
nullable
latest
required

The counters of every projects[].latestRun, added up.

object
passed
required
integer
failed
required
integer
broken
required
integer
blocked
required
integer
skipped
required
integer
byPriority
required

The results of the period by the priority their case has NOW, five rows in the order of the enum — which is the rank — a priority nobody ran being a row of zeros.

Array
>= 5 items <= 5 items
object
priority
required
string
Allowed values: low normal important high critical
passed
required
integer
failed
required
integer
broken
required
integer
blocked
required
integer
skipped
required
integer
projects
required
Array<object>

One project of the request — every one asked for is a row, data or none: a project with nothing in the period answers zeros and null, which the page reads as «No runs in the period».

object
id
required
string
name
required
string
role
required

What a member may do (B8-team). owner runs the project — members, roles, its name; member does everything else; reader is refused every write at the boundary, before a handler runs.

string
Allowed values: owner member reader
cases
required

Live cases by type, now — the period does not act on them; tags and suite do.

object
manual
required
integer
automated
required
integer
eval
required
integer
latestRun
required

The last FINISHED run of the project in the period, by when it finished — a launched or terminated run never. Its counters are the summary frozen when it finished; a run that arrived finished (plune sync, a Cairn ingest) is counted off its results, which is what that summary would hold. null — no finished run in the period.

object
id
required
string
finishedAt
required
string format: date-time
counts
required

One counter per result status, all five present — a status that did not occur is 0, never absent.

object
passed
required
integer
failed
required
integer
broken
required
integer
blocked
required
integer
skipped
required
integer
passRate
required

Passed ÷ every result of the period × 100, one decimal; null — no results.

number
nullable
flaky
required

Live cases whose stability is flaky or critical — the score the case list shows, over the last 50 results of each case. The period does not act on it: stability is the history of the case. tags and suite do.

integer
facets
required

The values the filters offer — of the projects alone, whatever else the request narrows by: a value chosen must not leave its own list. Labels and environments count live runs of all time, tags count live cases; people are the members of the projects.

object
labels
required
Array<object>
object
value
required
string
runs
required
integer
>= 1
tags
required
Array<object>
object
value
required
string
cases
required
integer
>= 1
environments
required
Array<object>
object
value
required
string
runs
required
integer
>= 1
people
required
Array<object>
object
userId
required
string
name
required
string
nullable
email
required
string format: email
Example
{
"range": {
"from": "2026-08-29T21:00:00.000Z",
"to": "2026-09-28T20:59:59.000Z"
},
"kpis": {
"passRate": 86.2,
"cases": 1998,
"runs": 42,
"manual": 202,
"automated": 1792,
"eval": 4,
"coverage": 89.9
},
"latest": {
"passed": 1629,
"failed": 88,
"broken": 9,
"blocked": 0,
"skipped": 66
},
"byPriority": [
{
"priority": "low",
"passed": 2900,
"failed": 60,
"broken": 10,
"blocked": 0,
"skipped": 120
},
{
"priority": "normal",
"passed": 33530,
"failed": 3100,
"broken": 420,
"blocked": 12,
"skipped": 1810
},
{
"priority": "important",
"passed": 4100,
"failed": 380,
"broken": 40,
"blocked": 0,
"skipped": 230
},
{
"priority": "high",
"passed": 3300,
"failed": 610,
"broken": 55,
"blocked": 4,
"skipped": 150
},
{
"priority": "critical",
"passed": 1420,
"failed": 190,
"broken": 12,
"blocked": 0,
"skipped": 40
}
],
"projects": [
{
"id": "u-shop::default",
"name": "Checkout",
"role": "owner",
"cases": {
"manual": 20,
"automated": 1180,
"eval": 4
},
"latestRun": {
"id": "6f1c2b3a-0000-4000-8000-0000000000d1",
"finishedAt": "2026-09-16T09:12:04.151Z",
"counts": {
"passed": 1109,
"failed": 38,
"broken": 6,
"blocked": 0,
"skipped": 27
}
},
"passRate": 89.1,
"flaky": 7
},
{
"id": "9c4e7d10-0000-4000-8000-0000000000a4",
"name": "Mobile app",
"role": "member",
"cases": {
"manual": 182,
"automated": 612,
"eval": 0
},
"latestRun": {
"id": "9c4e7d10-0000-4000-8000-0000000000d2",
"finishedAt": "2026-09-16T09:12:04.151Z",
"counts": {
"passed": 520,
"failed": 50,
"broken": 3,
"blocked": 0,
"skipped": 39
}
},
"passRate": 81.4,
"flaky": 12
}
],
"facets": {
"labels": [
{
"value": "nightly",
"runs": 12
},
{
"value": "release",
"runs": 4
}
],
"tags": [
{
"value": "smoke",
"cases": 214
},
{
"value": "checkout",
"cases": 88
}
],
"environments": [
{
"value": "staging",
"runs": 30
},
{
"value": "prod",
"runs": 12
}
],
"people": [
{
"userId": "u-shop",
"name": "Olena Kovalenko",
"email": "[email protected]"
},
{
"userId": "6f1c2b3a-0000-4000-8000-0000000000a2",
"name": null,
"email": "[email protected]"
}
]
}
}
Server-Timing
string

Sends a Server-Timing header (W3C): where the server spent the time, in milliseconds to a tenth — DevTools lists it under Network → Timing. auth is the token or session and the set of projects, counted from the start of the request; projects, flaky, priority and facets are the four reads of the database, each from the moment it is sent until its rows are back — they run side by side, so they do not add up; total is the whole answer, before it is sent. The names are fixed and carry nothing of yours.

Example
auth;dur=7.6, projects;dur=17.9, flaky;dur=20.6, priority;dur=24.0, facets;dur=30.2, total;dur=42.6

A bound that is not a date-time with a zone, from after to, suite without exactly one project, more than 100 projects

Media type application/json
object
error
required
string
Examples

More than 100 projects, named or yours

{
"error": "at most 100 projects per request"
}

No / invalid token or session

A project you are not a member of, or one in the bin — or, under a token, any project but its own; the same answer as an id nobody has

Media type application/json
object
error
required
string
Example
{
"error": "project 'u-shop::default' not found"
}