# Dashboard Gallery documentation
---
---
> Public gallery of Data Studio dashboards for RUCKUS One and SmartZone: browse redacted examples by vertical or domain, ask about them in plain language, and download an export bundle to import into your own tenant.
---
---
# What is the Dashboard Gallery?
Source: https://docs.neuralconfig.com/dashboard-gallery/
The Dashboard Gallery is a public collection of **Data Studio dashboards**,
published at `https://dashboards.lanpulse.com`. Each one was built against real
network telemetry, redacted, and published with the export bundle that recreates
it. You can browse them, ask about them in a sentence, and download a bundle to
import into your own tenant.
There is nothing to install and nothing to sign in to. Every page described in
these docs is open to anyone with the URL.
## What you can do with it
- **Browse and filter.** The gallery is a grid of dashboards with a filter rail
down the side. Filters live in the URL, so a filtered view is a link you can
send. See [Browse and filter the gallery](https://docs.neuralconfig.com/dashboard-gallery/browsing/).
- **Ask in plain language.** The search box takes a sentence rather than
keywords — *"anything on MAC table usage?"* — and answers only from what is
published here. See
[Ask about the dashboards](https://docs.neuralconfig.com/dashboard-gallery/asking/).
- **Download an export bundle.** Every published dashboard has a downloadable
ZIP you can import into Data Studio. See
[Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/).
- **Ask for one that isn't there.** If nothing matches, there is a form that
puts your request in front of the person who builds them. See
[Request a dashboard](https://docs.neuralconfig.com/dashboard-gallery/requesting/).
## Which platforms it covers
Data Studio ships in two places, and the gallery names both:
| Platform | Export bundle | In the gallery |
|---|---|---|
| **RUCKUS One** | Yes | Downloadable today |
| **RUCKUS AI (SmartZone)** | Not yet | Listed as a disabled control with a *"Coming soon"* note |
The unavailable platform is shown rather than hidden, because *"coming soon"* is
something you can act on where a missing button is not. The disabled control is
not what makes it unavailable — the download route refuses an unavailable
platform on the server. See
[Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/#the-two-platforms).
## What the gallery publishes, and what it doesn't
The gallery **publishes two artifacts per dashboard**: a screenshot and a
Data Studio export bundle (`dashboard_export_*.zip`). It does not generate
either one — both come out of Data Studio itself, which means a signed-in person
exports them and uploads them here. What the gallery does is everything that
happens after that: redacting the image, scrubbing the bundle, indexing what
survives, and serving the result.
:::caution[The original is never published]
The uploaded screenshot is kept under a private storage prefix and no public
route can construct a key into it. The published image is a **new file** with
opaque bars burned into it — the covered pixels were never written. See
[What is removed before publishing](https://docs.neuralconfig.com/dashboard-gallery/whats-removed/).
:::
## What it is not
- **Not a console.** You cannot connect a tenant to it, and nothing you do here
reaches your network.
- **Not a live view.** Each dashboard is a screenshot taken at a point in time,
plus the definition that rebuilds it. Numbers on the image are whatever they
were on the day it was captured.
- **Not signed in.** There is no reader account, no session and nothing stored
about you. The one thing you can write is a dashboard request, and that is
covered in [Request a dashboard](https://docs.neuralconfig.com/dashboard-gallery/requesting/).
## Explore the docs
- [Browse and filter the gallery](https://docs.neuralconfig.com/dashboard-gallery/browsing/): The card grid, the five kinds of category in the filter rail, shareable filter links, and the per-dashboard permalink.
- [Ask about the dashboards](https://docs.neuralconfig.com/dashboard-gallery/asking/): What the plain-language search can answer, why it takes one question at a time, and how a category question returns a whole set.
- [Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/): Pick a platform, download the ZIP, and import it in Data Studio under Dashboards → Import.
- [What is removed before publishing](https://docs.neuralconfig.com/dashboard-gallery/whats-removed/): Destructive image redaction, the fields deleted from the export bundle, and the audit that refuses to store a bundle that still matches a pattern.
- [Request a dashboard](https://docs.neuralconfig.com/dashboard-gallery/requesting/): The request form, the duplicate check that runs while you type, and what happens to a request after you send it.
- [Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/): Every rate limit, the daily ceiling on the search's model spend, and what still works once it is reached.
- [Public HTTP API](https://docs.neuralconfig.com/dashboard-gallery/api/): The unauthenticated endpoints behind the gallery, with their parameters and response shapes.
- [Troubleshooting](https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/): The exact messages the gallery returns, what each one means, and what to do next.
Need help? Reach the [support team](https://support.neuralconfig.com/dashboard-gallery?from=docs).
---
# Browse and filter the gallery
Source: https://docs.neuralconfig.com/dashboard-gallery/browsing/
The gallery is a grid of published dashboards with a filter rail beside it.
**Filters live in the URL**, so a filtered view is a link: whatever you select
appears as `?facet=` in the address bar, and sending that address sends the
selection with it.
## The card grid
Each card carries the dashboard's thumbnail, its title, a one-line summary and
up to three category chips — the **vertical** it belongs to, followed by up to
two **product** chips. The other categories a dashboard carries are on the
dashboard itself rather than the card, because a card has room for the two facts
people browse by.
Clicking a card opens the dashboard without leaving the page, and changes the
address to that dashboard's permalink.
## The five kinds of category
The rail groups categories by **kind**. There are five, and they fall into two
groups: three a person assigns, and two read out of the export bundle at upload.
| Kind | Label in the rail | Assigned by | Example values |
|---|---|---|---|
| `role` | Role | A person | Business leader, Network engineer, Help desk technician |
| `vertical` | Vertical | A person | Hospitality, MDU, Higher Ed, K-12, US Federal Government, MWL, Service Provider, LPV, Pro AV |
| `product` | Product | A person | Wi-Fi, Switching |
| `dataset` | Dataset | Read from the bundle | The Data Studio dataset each chart is built on |
| `viz` | Visualization | Read from the bundle | Table, Big number, and the other chart types used |
**Role sorts first** because it is the broadest way into the gallery — who a
dashboard is for. Every role value returns a real set; a vertical may return a
single dashboard.
**Dataset and Visualization start collapsed.** They are mechanical, there are
many near-identical values, and they are more useful for narrowing a search than
for starting one. The rail collapses them because the server marks those kinds
as derived, not because their names are special-cased in the page.
:::note
Categories are data, not code. A new kind or a new value is a row in the
gallery's database, and the rail renders whatever the server returns — so the
values above are the ones seeded today, not a fixed list.
:::
### A category with nothing behind it is hidden
Counts in the rail are over **published** dashboards only, so a category with a
count of zero is a dead end and the rail leaves it out. The one exception is a
category you currently have selected: dropping that silently out of a link
someone sent you would be worse than showing it.
## How selections combine
Selections are **ORed within a kind and ANDed across kinds** — the behaviour a
filter rail implies.
Selecting *Hospitality* and *Higher Ed* shows dashboards in either vertical.
Adding *Switching* to that narrows it to dashboards that are in one of those two
verticals **and** carry the Switching product tag. Selecting more categories
from different kinds can easily land on nothing, and the empty state says so:
> No dashboard carries every category selected. Clearing one of them will widen
> the results.
## A filter link that no longer resolves shows nothing
If a `?facet=` value in a bookmark names a category that no longer exists, the
gallery returns **no dashboards** — not the whole unfiltered gallery. A filter
that quietly widens into "everything" looks like a working page, which is the
worse failure: you would read a full gallery as the answer to a narrow question.
The rail names the dead value rather than dropping it in silence:
> Some filters in this link no longer exist: `d-sla`. **Remove them**
Clicking **Remove** drops just those values and leaves the rest of the selection
in place.
## Opening one dashboard
Every dashboard has a permalink at `/d/{slug}` — for example
`https://dashboards.lanpulse.com/d/rks-sw-l2`. That URL works three ways at
once: a person gets the dashboard open over the gallery, a link unfurler gets
the dashboard's own title, description and thumbnail, and a crawler gets a real
page. A slug that is not published answers `404`, and the gallery renders its
own not-found state rather than a bare error.
The open dashboard shows:
- **The full screenshot**, with **Fit**, **+** and **−** zoom controls stepping
through 1×, 1.5×, 2×, 3× and 4×. Double-clicking the image toggles between 1×
and 2×, and dragging pans — but only once the image is actually bigger than
the frame, so a dashboard that already fits does not move when you drag it.
- **The chart count**, beside the title in the top bar.
- **The summary and description.**
- **Every category it carries**, grouped and headed by kind — so the panel says
*Dataset* because the server does.
- **"Built on …"** — the internal dataset names, under the friendly labels
rather than instead of them. The chip says *Client Info and Statistics*
because that is what the Data Studio UI calls it; this line says
`binnedSessions` because that is what someone rebuilding the chart has to
type.
- **The export bundle block** — see
[Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/).
- **Previous** and **Next**, which move through the dashboards currently in the
grid.
## What the gallery counts
Opening a dashboard reports one **view** for it, and selecting a category
reports one selection for that category. These are reader numbers rather than an
audited figure, they are never shown publicly, and nothing about you is stored
with them — no account, no identifier, no session.
## Next steps
- [Ask about the dashboards](https://docs.neuralconfig.com/dashboard-gallery/asking/): When you do not know which category to pick, describe what you need in a sentence instead.
- [Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/): Take a dashboard you found and import it into your own tenant.
- [Public HTTP API](https://docs.neuralconfig.com/dashboard-gallery/api/): The same listing and filtering, as JSON — including every category with its count.
---
# Ask about the dashboards
Source: https://docs.neuralconfig.com/dashboard-gallery/asking/
The search box at the top of the gallery takes **a sentence, not keywords**.
Ask *"anything on MAC table usage?"* or *"which dashboards suit a help desk
technician?"* and you get a written answer plus the dashboards it is about, as
cards you can open.
It answers **only from this gallery**. The model behind it is shown nothing
about these dashboards except the rows a search returned, so it has no general
knowledge of them to fall back on — and when nothing fits, it says so rather
than offering the nearest thing as if it fit.
## What it can answer
- **"Is there one for X?"** — the common case, and the one the retrieval is
tuned for.
- **"What is here for a network engineer?"** — a question about a category. The
reply names a few and states the real total, and the cards carry the whole
set. See [Category questions return the whole set](#category-questions-return-the-whole-set).
- **"Anything on err-disabled ports?"** — a question about a *metric*, which is
usually the name of a chart rather than of a dashboard. Chart titles and
section headings are read out of each export bundle and indexed, so these
land.
- **"How many are tagged Wi-Fi?"** — a question about a size. The answer engine
is given the real total, not the number of rows it happened to retrieve.
Questions are capped at **800 characters**.
## How a question is answered
```mermaid
flowchart TB
Q["Your question"] --> TOOL["search_dashboards
keywords + optional category"]
TOOL --> FTS["Full-text ranking
title, summary, chart titles"]
TOOL --> SEM["Semantic ranking"]
Q -. "embedded exactly as typed" .-> SEM
FTS --> RRF["Reciprocal rank fusion"]
SEM --> RRF
RRF --> ROWS["Published rows"]
ROWS --> REPLY["The written answer"]
ROWS --> CARDS["The cards beside it"]
```
Reading that as sentences, because each relationship matters on its own:
1. **Your question goes to a search tool**, never straight to an answer. The
model turns it into keywords and an optional category, and calls
`search_dashboards`.
2. **Two rankings run over the same candidates.** One is a full-text ranking
whose columns are weighted — a dashboard carrying fifteen chart titles must
not outrank one whose own title is an exact match. The other compares your
question against a stored vector for each dashboard.
3. **Your own sentence is embedded as well as the model's rewrite**, and the two
semantic rankings are fused with each other before they meet the full-text
half. The words a rewrite drops are often the ones that would have found the
row.
4. **Reciprocal rank fusion combines the two halves.** A dashboard has to place
well in at least one of them to survive, which is where precision comes from.
5. **Only published dashboards are reachable.** The published check is written
into the search's own SQL, again into the query that loads the vectors, and
again when the rows are finally read — so a candidate cannot arrive through
the vector table and skip it. There is no argument the model can write that
reaches an unpublished draft.
6. **The written answer and the cards come from the same rows.** The cards are
selected from what the search returned; they are never parsed out of the
reply.
:::note[Why the cards cannot be wrong]
The prompt asks the model not to invent a dashboard. The card selection is what
makes it not matter if it does — **a dashboard that does not exist has no row,
so it cannot get a card.** Anything you can click on is something the search
actually found.
:::
## Every question is a fresh one
The endpoint **accepts no conversation history**. It is not validated and
rejected — it is simply never read. Anyone can post any body to a public
endpoint, and a forged previous *answer* is indistinguishable from one this site
really produced, which would make every rule about what may be asserted
negotiable.
So the box takes one question and answers it, and the interface matches: one
answer on screen, replaced by the next. Asking a follow-up means asking a whole
question — *"and which of those is for switching?"* has nothing to refer back
to.
## Category questions return the whole set
Asking what a category contains is a question about a **set**, so a category
search returns the entire tagged set rather than the first few. Two things
follow:
- **The reply states the real total.** It names a few of them in prose and tells
you how many there are. Before the total was available, the answer engine
reported the number of rows it had retrieved — five — as though five were the
answer, when sixteen matched.
- **Every one of them still gets a card**, even where the prose names three.
The cards are how you reach the rest of the set.
When an answer's cards are only part of a category, a chip appears under them:
> **Show all 16 in the gallery →**
It appears only when there is a rest to show — if the cards already are the
whole category, there is nothing to offer. The point of it is the **URL**: an
answer has no address, and a filtered gallery is a link you can send.
## What you see while it works
A question takes a few seconds, and the panel reports what is happening rather
than showing a bare spinner: which search is running, the terms it is running,
and how much matched. The answer itself arrives once, at the end.
## Limits worth knowing
| Limit | Value |
|---|---|
| Question length | 800 characters |
| Searches per question | 3 |
| Model turns per question | 4 |
| Wall clock for one model call | 40 seconds |
| Answer length | 1,200 tokens |
| Questions per minute, per address | 20 |
Two different models answer, depending on how much of the day's budget is left —
they were measured as equal on recall, so the swap costs precision rather than
coverage. Once the day's allowance is spent the box stops answering and says so,
and everything else on the site keeps working. Both are covered in
[Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/).
:::tip[When search is resting, the filters are not]
The category rail and the dashboards themselves have nothing to do with the
model. If the search box is unavailable, filtering still finds the same
dashboards — see [Browse and filter](https://docs.neuralconfig.com/dashboard-gallery/browsing/).
:::
## Next steps
- [Request a dashboard](https://docs.neuralconfig.com/dashboard-gallery/requesting/): When the honest answer is that nothing here fits, the answer panel offers to pass the request on.
- [Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/): The daily ceiling on model spend, what happens past it, and every rate limit.
- [Troubleshooting](https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/): The exact sentences the search returns when it refuses, and what each one means.
---
# Download an export bundle
Source: https://docs.neuralconfig.com/dashboard-gallery/downloading/
Every published dashboard can be downloaded as a **Data Studio export bundle** —
a ZIP of YAML documents — and imported into your own tenant. The download needs
no sign-in and no request: open a dashboard and the bundle is in the panel
beside it, under **Export bundle**.
## The two platforms
Data Studio ships in two places, and the panel lists both:
| Platform | Control | Why |
|---|---|---|
| **RUCKUS One** | A live download link | It has an export bundle format |
| **RUCKUS AI (SmartZone)** | A disabled button reading *"Coming soon — the converter is being written"* | RUCKUS AI has no bundle format of its own, so the shape a converter would emit is not yet settled |
The unavailable platform is **listed rather than hidden**, because "coming soon"
is information you can act on where a missing button is not.
:::note[The disabled button is not what makes it unavailable]
The download route resolves the platform through the same registry the panel
reads and answers `404` for one that is not available — on the server, before
anything is read from storage. The gallery's pages are static files served to
anyone who asks for them, so a disabled control could never be the thing
deciding this.
:::
## Download and import
1. **Open the dashboard** you want, from the grid or from its `/d/{slug}`
permalink.
2. **Click the platform button** under *Export bundle*. The file arrives named
after the dashboard's slug — `rks-sw-l2-export.zip`.
3. **In Data Studio, go to `Dashboards → Import`** and choose the ZIP.
4. **Open the imported dashboard.** Its charts bind to your own datasets and
render against your own data.
## What has already been done to the bundle
The file you download has been through two separate operations, and knowing
which is which explains what you will see after importing.
**Scrubbed once, when it was uploaded.** Identity fields are deleted, filters
pinned to tenant-scoped columns are dropped, and values with a recognisable
shape — MAC addresses, IP addresses, hostnames, emails, serial numbers — are
replaced with values from reserved documentation ranges. A bundle that still
matches a pattern after that is never stored at all. The full account is in
[What is removed before publishing](https://docs.neuralconfig.com/dashboard-gallery/whats-removed/).
**Re-identified on every download.** Each download gets a **fresh set of
dashboard and chart identifiers**. Dataset and database identifiers are left
exactly as they were.
### Why the identifiers are fresh every time
An import matches objects **by identifier, and those identifiers match globally
across tenants** — not just within one. If two people imported byte-identical
copies of the same bundle, the second import would not create a second
dashboard; it would update the first person's record and move its tenant
scoping to the importer.
So no two downloads of the same dashboard carry the same dashboard and chart
identifiers. One old-to-new map is built for the whole archive and then applied
across every file, which is what keeps a layout block pointing at the charts it
names. The response is sent `no-store` for the same reason: a cached copy handed
to a second downloader would reintroduce the exact collision this prevents.
**Dataset and database identifiers are deliberately excluded from that map.**
They are how the import binds to a real dataset, so they must survive both
operations unchanged.
## What to expect after importing
| You will see | Why |
|---|---|
| The dashboard's layout, charts and sections, intact | Structure is not sensitive and is not touched |
| Charts rendering against **your** data | Filters pinned to tenant-scoped columns such as `zoneName`, `ssid` and `apName` were removed rather than rewritten — a rewritten value would match no real row and the chart would render empty |
| Venue or site names still in chart *titles* and text | A name like *"Riverside"* or *"Home"* is an ordinary word with no shape a pattern can catch. These are reviewed by a person before publishing rather than caught by a rule |
| Documentation-range values in descriptions | `00:00:5e:00:53:01`, `192.0.2.4`, `host-1.example.com` and similar are replacements, not real values |
| Filter **dropdowns** on tenant-scoped columns still present | A dropdown control is part of the dashboard's structure, so it survives; only its pre-selected default is cleared |
:::caution[Datasets are referenced, not included]
The bundle carries no `datasets/` or `databases/` directory — datasets are
referenced by identifier only. The import binds to a dataset that already exists
in your tenant. If the dataset a chart names is not there, that chart has
nothing to bind to.
:::
## URLs
The canonical download URL is the bare one, and it means RUCKUS One:
```
https://dashboards.lanpulse.com/d/{slug}/download
```
The per-platform form resolves to the identical bytes for the default platform:
```
https://dashboards.lanpulse.com/d/{slug}/download/ruckus-one
```
Both are rate limited to **20 downloads per minute per address**, and both
refuse a dashboard that is not currently published. See
[Public HTTP API](https://docs.neuralconfig.com/dashboard-gallery/api/#get-dslugdownloadplatform).
## Next steps
- [What is removed before publishing](https://docs.neuralconfig.com/dashboard-gallery/whats-removed/): Exactly which fields are deleted, which filters are dropped, and the audit that gates the write.
- [Browse and filter the gallery](https://docs.neuralconfig.com/dashboard-gallery/browsing/): Find the dashboard whose bundle you want, by role, vertical, product or dataset.
- [Troubleshooting](https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/): A download that 404s, a 429, and what an empty imported chart means.
---
# What is removed before publishing
Source: https://docs.neuralconfig.com/dashboard-gallery/whats-removed/
Each dashboard reaches the gallery as two artifacts — a screenshot and a Data
Studio export bundle — and both are rewritten before anything is published. The
rule the whole design is built around is short:
**An unredacted original is never publicly reachable.**
That is enforced in the API rather than in the page. The gallery's own pages are
static files served to anyone who asks for them, so they can never be the thing
deciding what is allowed.
## The publish pipeline
```mermaid
flowchart TB
UP["Upload
screenshot + export bundle"] --> PRIV["Private storage
originals, never served"]
PRIV --> OCR["OCR proposes rectangles"]
OCR --> REVIEW["A person reviews them"]
REVIEW --> FLAT["Flatten to a new image file"]
PRIV --> SCRUB["Scrub the bundle"]
SCRUB --> AUDIT["Re-scan what was just written"]
AUDIT -->|"clean"| PUB["Public storage"]
AUDIT -->|"still matches"| STOP["Nothing is written"]
FLAT --> PUB
```
In sentences: the upload goes to **private storage**, where nothing public can
reach it. The image is redacted by OCR proposing rectangles, a person reviewing
them, and the browser flattening the result into a **new file**. The bundle is
scrubbed and then **audited by re-scanning what the scrubber just wrote** — if
the audit still finds a match, nothing is written to public storage at all.
Only the flattened image and a clean bundle reach the public side.
## The image: redaction is destructive
**Every bar in a published screenshot covers pixels that were never written into
the file.** The published image is not the original with rectangles drawn over
it — it is a new image, drawn to a canvas with opaque rectangles filled in and
saved. There is nothing underneath a bar to uncover.
Bars are **opaque, not blurred**. Blur on small text is a weaker guarantee than
it looks.
The detection step runs **in the browser**, and the OCR engine is served from
the gallery's own origin rather than a CDN — the unredacted image never leaves
the machine it was opened on. Each recognised word is checked against the same
pattern set the bundle scrubber uses, and a match becomes a *proposed*
rectangle that a person accepts, moves, resizes or deletes. Running detection
again only **adds** proposals; it never removes a bar placed by hand.
### Publishing with no bars at all
Some screenshots genuinely have nothing to cover, so publishing with zero
rectangles is allowed — but only after a second confirmation, recorded
explicitly. To publish, a dashboard needs a flattened image **and** either at
least one rectangle or that explicit record.
The rectangles are **counted**, not merely checked for: an editor run that
covered nothing still saves an empty list, and a list existing is not evidence
that anything was covered.
### What stays private
- **The original screenshot**, under a storage prefix that the code serving
public assets cannot construct a key into.
- **The rectangle coordinates.** They are kept only so a redaction can be
reopened and adjusted, and are never used to render a public view.
- **The scrub report** (below).
## The bundle: scrubbed once, on upload
Redacting only the screenshot would publish the same data in plain YAML — the
export carries tenant identifiers, owner identifiers, owner email addresses and
hardcoded filter values. So the bundle is rewritten too.
### Identity fields are deleted, not replaced
An import assigns ownership to whoever runs it, so there is nothing left for
these to do. A pseudonymised owner id would be a lie the importer then has to
notice.
`tenant_ids` · `tenant_id` · `owner_ids` · `owners` · `created_by` ·
`changed_by` · `created_by_fk` · `changed_by_fk` · `last_saved_by` · `email` ·
`username` · `user_id` · `certified_by` · `certification_details`
### Filters on tenant-scoped columns are dropped
Some columns hold tenant data **whatever value they contain**. A venue named
"Home" is an ordinary English word that no shape-based rule can find, so these
clauses are matched on the **column name** instead, and the whole clause is
removed:
`zoneName` · `venueName` · `venue` · `zone` · `ssid` · `networkName` ·
`wlanName` · `apName` · `apMac` · `apGroupName` · `apSerialNumber` ·
`clientMac` · `clientIp` · `userName` · `userId` · `deviceName` · `hostname` ·
`serialNumber` · `switchName` · `switchMac` · `tenantId` · `tenantName` ·
`accountName` · `customerName`
Matching is case-insensitive, because bundles are inconsistent about `zoneName`
versus `zonename`.
:::note[Dropped rather than pseudonymised, and that is deliberate]
A pseudonymised comparator would still be a value — just one matching no real
dimension value. The chart would import cleanly and render **empty**, which is
harder to notice than a filter that is plainly gone. Every drop is recorded in
the report so a person reviews it.
A filter **dropdown control** on one of these columns is different: it is part
of the dashboard's structure, so it survives, and only its pre-selected default
is cleared.
:::
### Values with a shape are replaced
Remaining strings are checked against the same pattern set the screenshot editor
uses. Each distinct real value maps to the same replacement every time, so a
filter still matches the chart beside it.
| What is matched | Replaced with | Range |
|---|---|---|
| MAC address | `00:00:5e:00:53:xx` | RFC 7042 documentation block |
| IPv4 address | `192.0.2.x` | RFC 5737 TEST-NET-1 |
| IPv6 address | `2001:db8::x` | RFC 3849 documentation prefix |
| Email address | `user1@example.com` | Reserved example domain |
| Hostname / FQDN | `host-1.example.com` | Reserved example domain |
| UUID / tenant id | `00000000000000000000000000000000` | — |
| Serial number | `SERIAL000001` | — |
Replacements are **shaped like what they replace** rather than being
`[REDACTED]`, for two reasons. A filter whose comparator became `[REDACTED]`
would import cleanly and then render nothing. And because they keep their shape,
the audit re-finds them all — which is how "scrubbed correctly" is told apart
from "still leaking".
:::caution[Serial numbers must carry a letter as well as a digit]
This is a deliberate under-match. Without the letter requirement, the pattern
matched the `1000000000` inside a bytes-to-GB divisor across ten charts of a
real bundle and rewrote it into `SERIAL000001` — valid YAML, broken arithmetic,
and no error until a person opened the chart. All-numeric serials are therefore
not detected. Under-matching surfaces in the editor for a person to fix;
over-matching silently corrupts a chart definition nobody re-reads.
:::
### Two places that are easy to miss, and are not missed
- **The nested copy.** One chart field holds a JSON document inside a YAML
string, carrying a second copy of the chart's parameters and filters —
tenant identifiers included. It is parsed, scrubbed by the same rules and
written back. A scrubber that walked the YAML tree alone would publish that
copy intact.
- **The dialect.** Files are re-serialised as **YAML 1.1**, which is what the
importer reads. A default YAML 1.2 writer leaves unquoted precisely what a 1.1
reader retypes: an exported timestamp comes back as a *date* rather than a
string, and the importer rejects the whole archive before reading a single
chart. The same gap turns `yes`/`no`/`on`/`off` into booleans and `10:30:00`
into a number.
### The audit refuses the write
The archive the scrubber just wrote is re-opened and scanned again over its raw
text. Replacement values are recognised as replacements rather than flagged
again. **If anything else still matches a pattern, nothing is written to public
storage at all** — storing it and relying on a published check to keep it
unreachable would put a leaking object in the public namespace with one query
between it and the world.
### The report, and what only a person can catch
The report lists every removed field, every dropped filter with the value it
held, every real-to-replacement pair, and any file that could not be inspected.
It stays with the record and is **never published**.
It is read before the dashboard is, because some things no rule here can find: a
venue name, a network name or a site label sitting in a chart title or a block
of prose. Those have no shape, and the read is what catches them.
## Why the boundary holds
| Mechanism | What it prevents |
|---|---|
| Two storage prefixes — originals private, published artifacts public | Nothing public shares a namespace with an original |
| Asset URLs take an id, a revision and one of three fixed filenames | There is no input that can address an object outside the public namespace |
| Every public query bakes `status = 'published'` into its SQL | A route cannot forget to filter for published-ness |
| Only the revision a record currently points at is served | A superseded, *less* redacted revision cannot be reached by guessing the previous number |
| Public responses list what goes out rather than deleting what must not | A new column is private until someone decides otherwise |
That last row is why original keys, the scrub report and the rectangle
coordinates are absent from every public response: they were never added to the
list of things that go out.
## Next steps
- [Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/): What the scrubbed bundle looks like once it is imported, and why its identifiers change on every download.
- [Public HTTP API](https://docs.neuralconfig.com/dashboard-gallery/api/): What a public response actually contains, field by field.
- [What is the Dashboard Gallery?](https://docs.neuralconfig.com/dashboard-gallery/): Where the two artifacts come from, and what the gallery does and does not generate.
---
# Request a dashboard
Source: https://docs.neuralconfig.com/dashboard-gallery/requesting/
When nothing in the gallery fits, you can describe what you need and the
gallery will pass it on. This is the **only thing on the site a visitor can
write**, and it needs no account.
## Where the form is
There is no standing "Request a dashboard" button, and that is deliberate: the
form appears at the moment you are already holding the thought.
- **Under a search answer.** When the search tells you the nearest thing it
has, the offer to request one is right there, and the form opens with your
question already in it.
- **On an empty grid.** When a filter combination returns nothing, the empty
state offers it.
- **At `#request`** on the gallery URL, which opens it with an empty field.
It opens **in place, under the header** — directly below the answer you are
deciding against — rather than as a dialog over it. A dialog would hide the very
answer you were reading.
## What the form asks for
| Field | Required | Limits |
|---|---|---|
| **What do you want on it?** | Yes | 10 to 1,200 characters |
| **Email** | No | Up to 254 characters, and it must look like an address |
The description is the whole of it. Naming the metric, the audience and the
question the dashboard should answer is what makes a request buildable — the
placeholder shows the shape:
> *Roaming failures by AP, so I can see which radios clients keep falling off.*
The email address is optional and exists only so the request can be answered. A
request with no address is a perfectly good request.
## Two things that happen while you type
**A duplicate check.** Once you have typed enough, the gallery runs your
half-written sentence through its own search and shows up to **three**
dashboards that may already cover it. It is the same retrieval the search box
uses, minus the model — so it costs nothing and takes no time worth noticing. If
it fails for any reason it shows nothing at all: a duplicate check that could
stop you asking for a dashboard would have inverted its own purpose.
When you reached the form from a search answer, the check does not re-run
immediately — the answer above **is** the duplicate check for that sentence.
It starts once you have changed enough of the draft for it to be your own
question rather than the one you arrived with.
**One clarifying question.** Once the draft reaches about 40 characters, the
gallery may ask a single question about it — the one thing that would most help
whoever builds it. Answering appends your answer to the draft. It is entirely
optional:
> *Or ignore this — the request sends either way.*
If the day's model allowance is spent, or anything else goes wrong, no question
appears and the form is unaffected.
## The bot check
Submitting requires a **Cloudflare Turnstile** check, verified on the server
against Cloudflare rather than taken from the page's word for it. The widget
loads **only when the form is opened**, so a visitor who never asks for a
dashboard never fetches a bot-detection script.
It is the one third-party script on the public site, and it is loaded from
Cloudflare rather than served from this origin — the script *is* the
verification, so a local copy would mint tokens Cloudflare then rejects.
:::note
A Turnstile token verifies exactly once and is good for five minutes. If a
submission is refused, the widget resets so your next attempt gets a fresh
token — otherwise the second try would be a guaranteed failure.
:::
The rate limit in front of the form — **6 submissions per minute per address** —
is a smoother, not the check. Turnstile is the check.
## After you send it
You get a confirmation and nothing else:
> **That is in the queue to be built.**
- **The request is never public.** There is no endpoint that reads requests
back — not even for the person who wrote it. Your words and your address are
the only things in this gallery that were *given* rather than published, and
the only route that can see them is behind the admin sign-in.
- **It arrives with the status `new`.** Only the site owner can move it on, to
`planned`, `built` or `declined`.
- **"N in the queue"** beside the dashboard count is the number of requests at
**`planned`** — reviewed and accepted for building. It is deliberately not
the number submitted, because anyone can submit and nobody but the owner can
promote. The segment is absent entirely when the number is zero, and it
disappears while a filter is applied, since the queue does not respond to
filters.
## If the form is not there
The gallery **hides the form when the bot check is not configured**, rather than
showing a form that cannot succeed. The server refuses independently either way,
so a form that appears is one that can work.
## Next steps
- [Ask about the dashboards](https://docs.neuralconfig.com/dashboard-gallery/asking/): Check whether the thing you are about to request already exists — the search is the fastest duplicate check there is.
- [Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/): The character limits, the submission rate limit, and the model allowance behind the clarifying question.
- [Troubleshooting](https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/): A refused verification, a request that is too short, and a form that will not appear.
---
# Limits and quotas
Source: https://docs.neuralconfig.com/dashboard-gallery/limits/
Every limit that can change what the gallery does, in one place. Most readers
never meet any of them; the one worth understanding in advance is the daily
model allowance behind the search box, because it is the only limit whose effect
lasts longer than a minute.
## Rate limits
All of these are **per client address, per minute**, and all of them return a
plain sentence rather than a silent failure.
| What | Limit | On refusal |
|---|---|---|
| Questions to the search box | **20 / minute** | `429` — *"Too many questions in a row — give it a minute."* |
| Dashboard requests | **6 / minute** | `429` — *"That is a lot of requests in a row — give it a minute."* |
| Bundle downloads | **20 / minute** | `429` — *"Too many downloads in a row — give it a minute."* |
| View beacons | **60 / minute** | `429` with an empty body |
The duplicate check and the clarifying question on the request form share the
request limit. Neither returns an error when refused — the duplicate check shows
no cards and the clarifier shows no question, because both are asides beside a
form that works without them.
:::caution[These are smoothers, not accounting]
Cloudflare documents the rate limiting binding as *"intentionally designed to
not be used as an accurate accounting system"* — its counters are per-location
and converge asynchronously. Measured against this site, it let **65 requests
from one address through in a minute without a single refusal**. That is fine
for what it is for. It is not a spending limit, which is why the spending limit
is a separate mechanism.
:::
## Content limits
| Where | Limit |
|---|---|
| A question to the search box | 800 characters |
| A dashboard request | 10 to 1,200 characters |
| An email address on a request | 254 characters |
| Draft length before a clarifying question is offered | 40 characters |
| Dashboards per API listing page | 60 by default, 100 maximum |
| Results the duplicate check shows | 3 |
## The answer engine's own caps
| Limit | Value | What happens at it |
|---|---|---|
| Searches per question | **3** | The search tool is **withheld**, not merely discouraged, and the model is told to answer from what it already has |
| Model turns per question | **4** | The turn budget ends and the reply is written |
| Wall clock for one model call | **40 seconds** | The call is aborted and the question returns a `503` |
| Answer length | **1,200 tokens** | The reply is cut. Citations are written last, and the gallery keeps whatever was written before the cut |
| Results from one search | 1 to 12, default 5 | — |
| Results from a **category** search | The whole tagged set, up to 60 | A question about a category is a question about a set, so it is not cut to the default |
## The daily model allowance
The search box is unauthenticated and calls a language model, which means an
unmetered visitor could write the account's inference bill. So there is a hard
ceiling on what all of the day's questions together may spend.
| | |
|---|---|
| **The ceiling** | **$1 per UTC day** |
| In Cloudflare's unit | About **90,900 neurons**, at the published rate of $0.011 per 1,000 |
| A measured question | About **18 neurons** |
| Questions that buys | Roughly **1,230 per day** |
| Checked | Before the model runs |
| Recorded | After it runs, whether it succeeded or not |
The unit is the **neuron** because that is what Cloudflare bills and what each
completion reports back — so the ledger is a record of money rather than an
estimate from token counts, and it stays true across a price change or a model
swap.
### Why 1,230 questions rather than 780
Two models answer, and which one you get depends on how much of the day is left:
| | Model | When |
|---|---|---|
| **Primary** | DeepSeek v4 Flash | Until **75%** of the day's allowance is spent |
| **Economy** | GLM 4.7 Flash | For the remaining 25% |
Over a 30-question set they scored **the same on recall** — each found every
dashboard it should have, and each refused every question the gallery has no
answer for. So the choice was never about coverage. DeepSeek is more precise
(95% against 85%) and far steadier at the slow tail (11.8 seconds at the 95th
percentile against 32.3); GLM costs about a third as much to run.
Taking both means the allowance buys **~583 DeepSeek questions and then ~650
GLM ones** instead of 780 DeepSeek ones, and the end of a busy day degrades to a
slightly less precise answer rather than to no answer. Which model runs is read
once per question, from the same spend figure the ceiling check already
fetched — so the two cannot disagree, and one question is answered by one model
the whole way through.
## What happens once the allowance is spent
The search box returns a `429` and says so:
> Search has used up today's allowance and is resting until tomorrow — the
> filters below still work.
**Everything that is not the model keeps working.** The categories, the filter
rail, every dashboard, every permalink and every download are unaffected, and
the duplicate check on the request form drops to text-only matching rather than
refusing. The degraded state is the site minus one feature, not a broken page.
The allowance resets at **00:00 UTC**.
:::note[The ledger counts failures too]
What a call consumed is recorded in a `finally` block, so a question that timed
out or failed still counts against the day. A ledger that counted only successes
would be one you walk past by making the model fail.
:::
## Caching
| Resource | Policy |
|---|---|
| Dashboard images and thumbnails | Immutable for one year — a tightened redaction publishes at a new revision, so the old URL is simply never requested again |
| A dashboard permalink's HTML | 60 seconds, then revalidated |
| The sitemap | 10 minutes |
| **An export bundle** | **`no-store`** — never cached, because two downloaders must not receive the same identifiers |
## Next steps
- [Ask about the dashboards](https://docs.neuralconfig.com/dashboard-gallery/asking/): What the search does with a question before any of these caps apply.
- [Public HTTP API](https://docs.neuralconfig.com/dashboard-gallery/api/): Which endpoint each of these limits sits in front of.
- [Troubleshooting](https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/): Telling a rate limit apart from a spent allowance — both are a 429.
---
# Public HTTP API
Source: https://docs.neuralconfig.com/dashboard-gallery/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](https://docs.neuralconfig.com/dashboard-gallery/limits/).
Base URL: `https://dashboards.lanpulse.com`
| Endpoint | Method | What it does |
|---|---|---|
| [`/api/dashboards`](#get-apidashboards) | `GET` | List published dashboards, optionally filtered |
| [`/api/dashboards/{slug}`](#get-apidashboardsslug) | `GET` | One published dashboard |
| [`/api/facets`](#get-apifacets) | `GET` | The category taxonomy, with published counts |
| [`/api/chat`](#post-apichat) | `POST` | Ask a plain-language question |
| [`/api/requests`](#post-apirequests) | `POST` | Submit a dashboard request |
| [`/api/requests/similar`](#post-apirequestssimilar) | `POST` | The duplicate check |
| [`/api/requests/clarify`](#post-apirequestsclarify) | `POST` | One clarifying question about a draft |
| [`/api/dashboards/{slug}/view`](#post-apidashboardsslugview) | `POST` | The view beacon |
| [`/d/{slug}/download/{platform}`](#get-dslugdownloadplatform) | `GET` | The export bundle |
| [`/assets/{id}/{rev}/{file}`](#get-assetsidrevfile) | `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
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=20
```
Returns `{ "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 |
:::note[An unresolvable filter returns nothing, not everything]
If a `facet` value names no category, the response is an empty list. It is never
the whole unfiltered gallery — a filter that quietly widens is the more
dangerous failure, because a full response looks like a working page.
:::
## `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
The whole category taxonomy, plus the configuration the gallery's page needs
before it paints.
```json
{
"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 }
}
```
- **`count` is over published dashboards only**, so a category with a count of
zero has nothing public behind it.
- **`derived` marks the kinds read out of an export bundle** — `dataset` and
`viz` — 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.planned`** is the number of requests accepted for building. It is
the only public read of the request table there is.
## POST /api/chat
Ask a question in plain language.
```json
{ "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:
```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
Submit a dashboard request. **This is the only public write that stores text you
typed**, and the only one with a bot check.
```json
{ "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
The duplicate check. Runs the gallery's own retrieval over a draft — no model
call.
```json
{ "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
Asks one question about a request draft.
```json
{ "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`
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}`
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}`
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
| Path | |
|---|---|
| `/sitemap.xml` | The gallery, the how-it-works page and every published permalink |
| `/robots.txt` | Allows everything except `/admin` |
## Next steps
- [Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/): The rate limit and daily allowance sitting in front of these endpoints.
- [Browse and filter the gallery](https://docs.neuralconfig.com/dashboard-gallery/browsing/): What the facet ids in these responses mean, and how selections combine.
- [Troubleshooting](https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/): Every error string these endpoints return, and what each one means.
---
# Troubleshooting
Source: https://docs.neuralconfig.com/dashboard-gallery/troubleshooting/
Every message below is quoted as the gallery actually returns it.
## The search box
### "Too many questions in a row — give it a minute."
You have asked more than **20 questions in a minute** from one address. Wait a
minute. Nothing is wrong and nothing is lost.
### "Search has used up today's allowance and is resting until tomorrow — the filters below still work."
The day's model allowance is spent. This is not a rate limit and waiting a
minute will not clear it — the allowance resets at **00:00 UTC**.
Both of these arrive as a `429`, so the sentence is how you tell them apart. The
filter rail, the dashboards, the permalinks and every download are unaffected;
see [Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/#what-happens-once-the-allowance-is-spent).
### "Search is unavailable at the moment. Try the filters instead."
The model call failed or ran past its **40-second** ceiling. This one is worth
retrying — the underlying cause is usually transient.
### "keep it under 800 characters"
The question is too long. Questions are capped at 800 characters.
### The answer says nothing matches, but I can see one that does
Two things are worth checking before concluding the search is wrong:
- **Ask with the words that are on the dashboard**, including the metric. Chart
titles and section headings are indexed, so *"err-disabled ports"* finds a
dashboard whose own title never says it.
- **Name the category.** *"switch related dashboards"* is answered better by
naming the Switching tag than by describing it — a dashboard can be about a
subject without the word appearing in its text.
If it still does not surface, the fastest route is the filter rail: categories
are exact where a search is a ranking.
### I asked a follow-up and it did not understand
The search takes **one question at a time** and keeps no history — *"and which
of those is for switching?"* has nothing to refer back to. Ask the whole
question. This is deliberate and is explained in
[Ask about the dashboards](https://docs.neuralconfig.com/dashboard-gallery/asking/#every-question-is-a-fresh-one).
## The gallery
### A filter link shows no dashboards
Two different causes, and the rail distinguishes them:
- **"Some filters in this link no longer exist: `d-sla`."** The link names a
category that has since been removed. Click **Remove** to drop just those and
keep the rest of the selection.
- **"No dashboard carries every category selected."** Every category still
exists, but nothing carries all of them at once. Selections are ANDed across
kinds, so clearing one widens the results.
### "There is no published dashboard at /d/…"
The slug does not name a currently published dashboard. It may never have
existed, or it may have been unpublished — an unpublished dashboard serves
nothing, including its images and its bundle, even though the files still exist
in storage.
### A dashboard image will not load
Image URLs carry a **revision number**, and only the revision the record
currently points at is served. An older URL — from a cached page, or from
editing the number by hand — returns `404`. Reload the gallery to pick up the
current one.
## Downloads
### The RUCKUS AI (SmartZone) button is greyed out
That platform has no export bundle yet, and the button carries the note
*"Coming soon — the converter is being written"*. Constructing the URL by hand
does not work around it: the route resolves the platform on the server and
answers `404`. See
[Download an export bundle](https://docs.neuralconfig.com/dashboard-gallery/downloading/#the-two-platforms).
### "Too many downloads in a row — give it a minute."
More than **20 downloads in a minute** from one address. Each download rewrites
every identifier in the archive, so this bounds the work rather than your
access — wait a minute and the same file is still there.
### The download 404s
The dashboard is not currently published, the platform is not available, or the
slug does not exist.
### Two people downloaded the same dashboard and got different files
**That is correct.** Dashboard and chart identifiers are regenerated on every
download, because an import matches objects by identifier and those identifiers
match globally across tenants — two identical copies would collide, with the
second import overwriting the first person's dashboard. Dataset and database
identifiers are the same in every copy, which is what binds the import to a real
dataset.
### An imported chart renders empty
Check what the chart is filtered on. Filters pinned to tenant-scoped columns —
`zoneName`, `ssid`, `apName` and others — are **removed** from the bundle before
publishing, so a chart should render against your own data. A chart that comes
up empty anyway is usually one whose dataset does not exist in your tenant:
bundles reference datasets by identifier and do not contain them.
### The imported dashboard still shows a venue or site name
In a chart *title* or a block of text, that is expected and is not a leak of
your data — it is a name from the original, which has no shape any pattern can
match. These are reviewed by a person before publishing rather than caught by a
rule. Values that *do* have a shape are replaced with documentation-range ones:
a `192.0.2.x` address or a `host-1.example.com` hostname is a replacement, not a
real value. See
[What is removed before publishing](https://docs.neuralconfig.com/dashboard-gallery/whats-removed/).
## The request form
### "That verification did not go through. Refresh the page and try again."
The bot check failed. A Turnstile token verifies exactly once and lasts five
minutes, so the usual causes are a token that was already used or has expired.
Refreshing the page is the reliable fix.
### "Say a little more about what you want on it."
The description is under **10 characters**. Naming the metric and who it is for
is what makes a request buildable.
### "Requests are closed at the moment. Try again later."
The bot check is not configured on the server, and the endpoint fails closed
rather than accepting unverified submissions. There is nothing you can do from
the page; [contact support](https://support.neuralconfig.com/dashboard-gallery?from=docs) if it persists.
### There is no request form anywhere
The gallery hides the form entirely when the bot check is not configured, rather
than showing one that cannot succeed.
### The "already covered?" cards do not appear
They need about **10 characters** to start, and they show nothing at all on any
failure — deliberately, since a duplicate check that blocks the form would have
inverted its own purpose. The form still sends.
### No clarifying question appeared
It needs a draft of about **40 characters**, and it is skipped when the day's
model allowance is spent. It is optional in every case — the request sends
either way.
## Next steps
- [Limits and quotas](https://docs.neuralconfig.com/dashboard-gallery/limits/): Every limit behind the messages on this page, with its exact value.
- [Public HTTP API](https://docs.neuralconfig.com/dashboard-gallery/api/): The status code each endpoint returns alongside these messages.
- [What is the Dashboard Gallery?](https://docs.neuralconfig.com/dashboard-gallery/): Start here if something on this page did not match what you expected the site to do.