Integration guide

Read a Quven server from your own dashboard

Issue a read-only key and point Homepage, Grafana or a script at your server: the six reads, both ways to send the key, and what the API will not tell you.

By Quven Editorial Team Updated 25 August 2026 Verified 25 August 2026

Most home dashboards already carry tiles for the NAS, the router and whatever else runs in the house. Quven can sit beside them. There’s a small read-only API built for exactly that, and this is how you use it.

The short version: you issue a key from the server’s settings, you send it as a header, and you get back what the server is holding and what it is currently playing. The key can’t change anything. And that isn’t a policy written in a document somewhere. It’s how the routes are built.

Issue the key

Open Settings, go to Server, and find the integration keys card. Give the key a name that will still mean something to you in six months. “Homepage” is fine. But “test2” is how you’ll end up with four keys and no idea which is which.

The secret appears once, in the panel that opens right after you issue it, so copy it then, because you won’t be able to read it back later, and that’s deliberate: the server keeps only a hash of it, so a stolen database gives an attacker nothing to replay. What the list shows afterwards is the last few characters, enough for you to tell two keys apart.

If you lose one, revoke it and issue another. Revocation takes effect immediately and can’t be undone.

Send it with the request

Two ways, whichever your tool supports:

curl -H "X-Quven-Key: qvn_your-secret-here" \
  http://your-server:5181/api/integration/v1/statistics

curl -H "Authorization: Bearer qvn_your-secret-here" \
  http://your-server:5181/api/integration/v1/statistics

Most dashboards will only give you an Authorization field, which is why the bearer form works too. If you happen to send both, the dedicated header wins.

What you can ask for

Six things, all GET. Four answer JSON, one answers Prometheus text, and the sixth pages what has already been played, and the field-by-field reference lives at quven.tv/api/integration/v1, with the OpenAPI document beside it if your client generates code. It’s built from the server’s own routes, so it describes the server you’re running, whichever version that is.

/api/integration/v1/server will tell you which installation you’re talking to: its name, its version, when the process started, and the API version it speaks. Read that last one if you write something you intend to share; it’s how you’ll find out whether a given server can understand you.

/api/integration/v1/statistics is the tile most people want. Films, series, episodes, files, bytes on disk.

{
  "movieCount": 2284,
  "showCount": 91,
  "episodeCount": 4137,
  "fileCount": 6602,
  "totalStorageBytes": 41203847561216
}

/api/integration/v1/libraries will list the libraries with a file count and the last time each finished a scan. It counts files, so a library that holds none isn’t in the answer yet; it appears with the first scan that finds something.

/api/integration/v1/sessions is what’s playing right now, with the title, the position, the runtime and whether the server is transcoding. How often should you ask? The server tells you, in refreshAfterSeconds. Please honour it. A dashboard that polls every second is the single fastest way you could make a media server feel broken.

/api/integration/v1/metrics is the same counts in Prometheus exposition format, so Grafana can scrape the server directly instead of going through an exporter somebody has to write.

/api/integration/v1/history pages what has already been played, most recent first, with limit and offset. It says what the server played and when. But it doesn’t say who asked for it.

The reference at quven.tv/api/integration/v1 will run these reads against your own server: put its address and a key in the bar at the top and press Send. The request goes from your browser to your server and nowhere else. Three things stop it, and the page says which it can: a server on plain http reached from that page, unless it’s on this machine; an address your browser can’t reach; and a server older than the console, which doesn’t admit that page yet. The curl button covers all three.

Every read carries an ETag. Send it back as If-None-Match and a server that hasn’t changed answers 304 with no body, which over a relay is the difference between a poll costing a round trip and costing a library listing, although sessions are the exception in practice: the answer carries the instant it was taken, so it differs every time and you’ll always get the body.

What it will not tell you, on purpose

Two omissions are deliberate, and worth knowing before you go looking for them.

The libraries endpoint carries no folder paths. Your disk layout is yours, and an integration has no business knowing, however harmless it would look on a dashboard tile, that the films live on /mnt/tank/media/films. The list is built from the catalogue statistics, and the library configuration is never read for it, precisely so that path can never leak into it.

The sessions endpoint carries no viewer identity. You’ll see that something is playing and what it is, not who is watching it, because asking what your server is doing and asking who in the house is watching what are different questions, and only the first one is a dashboard’s business.

Checking the server is up

GET /health/live answers 200 with no body and takes no key at all, so a monitor can poll it without holding a secret. /health/ready also exists, but it requires authentication and it isn’t meant for that. Uptime itself is in the reads: startedUtc on the server route, and quven_server_start_time_seconds in the metrics.

Limits, and why they are there

A key is allowed 120 requests a minute, and because the count is per key, that matters if you run more than one integration from the same machine: a misconfigured one burns its own budget and leaves the others alone.

A server holds 25 live keys at once. So if you reach that ceiling, you’ve almost certainly forgotten to revoke something.

About versioning

The routes live under /api/integration/v1, deliberately apart from the ones the Quven apps use. Those move when a client needs them to, which is fine for code that ships together and useless to a dashboard you configured once and forgot. This path is the one I intend to keep stable, and it should outlive several client releases.

A server older than the API answers 404 on all of it, cleanly, and that’s why /server returns an apiVersion: ask that first and you know what you’re dealing with.

One more thing about safety

A leaked key costs you something, though not what people usually fear. Somebody holding your key can read your catalogue and see what’s playing, but they can’t start a scan, change a library, touch a file, or issue themselves another key. Key management sits behind an owner login and nothing else, which means revoking the original genuinely ends it.

That boundary is the reason the API exists in this shape at all. Handing a dashboard your account token would have been much less work and a much worse idea.

Bring your library home.

Free for home use and yours to run on your own hardware. Install it in minutes.