Public HTTP API
The gallery’s own pages are built on a public, unauthenticated JSON API. There is no key to obtain and no sign-in, and every endpoint here is subject to the limits in Limits and quotas.
Base URL: https://dashboards.lanpulse.com
| Endpoint | Method | What it does |
|---|---|---|
/api/dashboards | GET | List published dashboards, optionally filtered |
/api/dashboards/{slug} | GET | One published dashboard |
/api/facets | GET | The category taxonomy, with published counts |
/api/chat | POST | Ask a plain-language question |
/api/requests | POST | Submit a dashboard request |
/api/requests/similar | POST | The duplicate check |
/api/requests/clarify | POST | One clarifying question about a draft |
/api/dashboards/{slug}/view | POST | The view beacon |
/d/{slug}/download/{platform} | GET | The export bundle |
/assets/{id}/{rev}/{file} | GET | Images and stored bundles |
Only published dashboards are reachable through any of them. The published check is written into the SQL of every query rather than applied by whatever calls it.
GET /api/dashboards
Section titled “GET /api/dashboards”Lists published dashboards, newest publication first.
| Parameter | Type | Default | Notes |
|---|---|---|---|
facet | string, repeatable | — | A category id. Repeat it to select several; values are ORed within a kind and ANDed across kinds |
limit | number | 60 | Capped at 100 |
offset | number | 0 | Negative values are treated as 0 |
GET /api/dashboards?facet=r-network-engineer&facet=d-switching&limit=20Returns { "dashboards": [ … ] }. Each entry is the same shape everywhere it
appears in this API:
| Field | Type | Notes |
|---|---|---|
slug | string | The permalink segment — /d/{slug} |
title | string | |
summary | string | null | |
description | string | null | |
image | string | Path to the full redacted screenshot |
thumb | string | Path to the thumbnail |
download | string | The canonical RUCKUS One bundle path |
downloads | array | One entry per platform — { id, label, available, note?, href }, with href null when unavailable |
image_w, image_h | number | null | Pixel dimensions of the full image |
chart_count | number | null | Charts read out of the bundle |
viz_types | string[] | Chart types used |
datasets | string[] | Internal dataset names — the identifiers stay server-side |
facets | array | { kind, label, slug } per category |
published_at | number | null | Epoch milliseconds |
GET /api/dashboards/{slug}
Section titled “GET /api/dashboards/{slug}”One dashboard, in the shape above, as { "dashboard": { … } }. A slug that is
not published returns 404 with { "error": "not found" }.
GET /api/facets
Section titled “GET /api/facets”The whole category taxonomy, plus the configuration the gallery’s page needs before it paints.
{ "kinds": [ { "kind": "role", "label": "Role", "sort": 5, "derived": false } ], "facets": [ { "id": "r-network-engineer", "kind": "role", "label": "Network engineer", "slug": "network-engineer", "count": 10 } ], "suggestions": [ "…" ], "examples": [ "…" ], "demos": [ { "slug": "…", "prompt": "…" } ], "requests": { "enabled": true, "site_key": "…", "planned": 3 }}countis over published dashboards only, so a category with a count of zero has nothing public behind it.derivedmarks the kinds read out of an export bundle —datasetandviz— rather than assigned by a person. The gallery collapses those groups in its rail from this flag rather than from a list of kind names in the page.requests.plannedis the number of requests accepted for building. It is the only public read of the request table there is.
POST /api/chat
Section titled “POST /api/chat”Ask a question in plain language.
{ "message": "which dashboards suit a help desk technician?" }| Field | Required | Notes |
|---|---|---|
message | Yes | 1 to 800 characters |
A history field is never read. It is not validated and rejected — there is
simply no path by which a conversation could reach the model. Every request is a
single fresh turn.
The default response is JSON:
{ "reply": "…", "dashboards": [ /* the same dashboard shape as above */ ], "filter": { "id": "d-switching", "label": "Switching", "count": 16 }}filter is the category the answer’s dashboards all share, with the number that
carry it — or null. It is present only when every returned dashboard carries
the category, since one question may search several.
Sending Accept: application/x-ndjson returns progress instead: one JSON object
per line, with t set to turn, search or results as the work happens, and
a final answer line carrying the same three fields. An error line replaces
it on failure.
| Status | Meaning |
|---|---|
400 | Empty message, over 800 characters, or an unparseable body |
429 | Rate limited, or the day’s model allowance is spent — the message says which |
503 | The model was unavailable or the call timed out |
POST /api/requests
Section titled “POST /api/requests”Submit a dashboard request. This is the only public write that stores text you typed, and the only one with a bot check.
{ "body": "Roaming failures by AP…", "email": "you@example.com", "token": "…" }| Field | Required | Notes |
|---|---|---|
body | Yes | 10 to 1,200 characters |
email | No | Up to 254 characters, shape-checked |
token | Yes | A Cloudflare Turnstile token, verified server-side |
Returns 201 with { "ok": true, "id": "…" }. The id is all you get back —
there is no endpoint that reads a request, so there is nothing to fetch it
with.
| Status | Meaning |
|---|---|
400 | Too short, too long, or a malformed email address |
403 | The Turnstile verification did not pass |
429 | Rate limited |
503 | The bot check is not configured — the endpoint fails closed |
The body is validated before the token is verified, because a token verifies exactly once: refusing a too-short request afterwards would leave you holding a spent token and a form you cannot resubmit.
POST /api/requests/similar
Section titled “POST /api/requests/similar”The duplicate check. Runs the gallery’s own retrieval over a draft — no model call.
{ "query": "roaming failures by AP" }Returns { "dashboards": [ … ], "total": n }, at most 3 dashboards. A query
under 10 characters returns an empty list, and so does any failure: this
endpoint never reports an error to the page, because a duplicate check that
blocks a form has inverted its own purpose.
POST /api/requests/clarify
Section titled “POST /api/requests/clarify”Asks one question about a request draft.
{ "draft": "Roaming failures by AP…" }Returns { "question": "…" }, or { "question": null } when the draft is under
40 characters, the day’s model allowance is spent, the caller is rate limited,
or anything at all goes wrong.
POST /api/dashboards/{slug}/view
Section titled “POST /api/dashboards/{slug}/view”The view beacon. No body is read; the slug is in the path.
Returns 204. A slug that is not published returns 404, so a counter can only
ever name something the gallery actually publishes. It is ungated on purpose —
no gate makes a client-reported counter unforgeable without putting a
bot-detection script on every dashboard open, which is a cost every reader would
pay to protect a number nobody bills against.
GET /d/{slug}/download/{platform}
Section titled “GET /d/{slug}/download/{platform}”The export bundle. The bare /d/{slug}/download means ruckus-one and is the
canonical URL.
| Platform | Available |
|---|---|
ruckus-one | Yes |
ruckus-ai | No — returns 404 |
Responds with application/zip, Content-Disposition: attachment, and
Cache-Control: no-store. Every response is different: dashboard and chart
identifiers are regenerated per request, while dataset and database identifiers
are preserved. An unknown platform, an unknown slug, an unpublished dashboard
and a dashboard with no stored bundle all return 404.
GET /assets/{id}/{rev}/{file}
Section titled “GET /assets/{id}/{rev}/{file}”Serves a published dashboard’s artifacts. file is one of full.webp,
thumb.webp or export.zip; anything else is a 404.
The route never accepts a storage key — it takes an id, a revision and one of
those three filenames and constructs the key itself, so there is no input that
can address anything outside the public namespace. It serves only assets
belonging to a currently published dashboard, and only at the revision that
record currently points at: an earlier revision number is a 404, because the
reason a redaction gets tightened is that the previous one covered too little.
Responses are cached immutably for one year.
Other public paths
Section titled “Other public paths”| Path | |
|---|---|
/sitemap.xml | The gallery, the how-it-works page and every published permalink |
/robots.txt | Allows everything except /admin |