One link per client, and nothing else on it
Tick the sites that belong to a client, copy the link that appears, send it. They open a page with their own sites on it, in read-only, with no account and no seat to pay for. Everything below is what that costs you and what it cannot do.
What the client opens
A single page, no navigation, no vocabulary they would have to learn. It answers the three questions a client actually has, in the order they have them: is my site working, has it been working this month, and when was it not.
- A banner at the top that says all fine, or names how many of their sites are not answering.
- One row per site: name, domain, a status dot, a bar chart of the last twenty-four hours, and the thirty day availability as a percentage.
- A table of recent interruptions: which site, when it started, how long it lasted, and whether it is over.
- One sentence saying that a single failed measurement is not counted as an interruption, because that is the question a client asks the first time they see the table.
A paused monitor shows as paused rather than as a failure. Monitoring you suspended on purpose is not an incident for the person paying for the site, and counting it as one is how a report loses its reader.
What decides the colour of a dot is on the other side of the wall, and it is a longer story: what the engine watches on the page, and what it does when it changes.
What the link cannot show them
This is the half that decides whether you send the link at all. An agency is not unsure about the value of a client dashboard; it is unsure about what the dashboard leaks.
| What | On the client link | Why it is that way |
|---|---|---|
| Any site belonging to another client | Never | Each read carries the client id in the query. There is no list to filter down to. |
| Your monitor settings, thresholds and alert rules | Never | The client screen renders no form and no configuration at all. |
| Who else you watch, and how many | Never | The failure counter in the page header is theirs, not the portfolio total. |
| Any way to change anything | None | Every write action lives behind the login and a CSRF token, so a link cannot reach one. |
| Their own history after you rotate the link | Kept | The history hangs off the client record, not off the token that opens it. |
The last row is the one people get wrong when they build this themselves. If the history is keyed on the credential, rotating the credential throws away the twelve months of availability you were about to invoice for.
The separation is in the query, not in the page
Most read-only dashboards are a normal dashboard with things hidden. That works until one request forgets to hide something. Here the filtering happens before the data leaves the database, so there is nothing to forget.
Every read carries the client id
This is the query behind the list of sites, copied from the engine rather than described:
SELECT s.id, s.name, s.domain, s.cms, m.status, m.uptime_30d, ...
FROM sites s
LEFT JOIN monitors m ON m.site_id = s.id AND m.role = 'primary'
WHERE s.client_id = ?
ORDER BY s.name ASC
The same clause appears in the overview, in the incident list and in the monitor ids used for the little bar charts. The value bound to that placeholder comes from the client record the token resolved to, never from the request. There is no parameter on the page to tamper with, because the page reads none. Read src/Client.php.
The client space is served before the login exists
In the engine front controller, the block that answers a client link runs and exits before the session is even started. Access without an account is not a tolerance carved out of the admin area: it is a separate path that never reaches the admin area at all. Nothing behind the login can be reached from it, because the code behind the login has not run.
An unknown token, a malformed one and a disabled one all produce the same 404 with the same body. Distinguishing them would turn the page into a way of finding out which tokens exist.
What the test suite holds in place
The engine ships its own runner, and the section covering this feature runs 42 checks out of 808, counted by running it on 1 August 2026 rather than by reading a number off a page. Nine of them are malformed tokens, including an SQL fragment, a path traversal and the right token in the wrong case. The others cover the parts that are easy to get wrong later: reattaching a site moves it instead of duplicating it, rotating a token detaches nothing, deleting a client keeps its sites and their history.
Run it yourself: bin/selftest.php, section Mode agence. It needs no dependencies and no Docker, which is the same reason the engine runs on shared hosting.
What the other seven answer to this
The usual answer to the same need is a status page, and a status page is a genuinely different object: it is public, it is one page for everybody, and one of the seven makes a much better one than we do. A scoped link is the other shape of the answer. An agency normally wants the second, and a client with a compliance page in mind wants the first.
| Tool | Their answer, as read on their own page | Read on |
|---|---|---|
| UptimeRobot | No dated reading on this point | Not read |
| Uptime Kuma | Status pages | 2026-07-29 |
| Better Stack | No dated reading on this point | Not read |
| StatusCake | No dated reading on this point | Not read |
| Pingdom | No dated reading on this point | Not read |
| Site24x7 | Not announced | 2026-07-29 |
| Hyperping | Status pages | 2026-07-29 |
| UptimeEZ | One link per client, scoped to their sites, no seat to pay for | The source |
Four of the seven carry no reading on this point, and that is a gap in our notes rather than a finding about them: UptimeRobot, Better Stack, StatusCake, Pingdom. Not announced is not the same as cannot do it, and no reading taken is weaker still. Anyone who can point at a page saying otherwise should, including in their favour.
All seven side by side, on fourteen criteria, or how to test a monitoring tool before you trust it.
Why this is the line that changes a portfolio
Thirty client sites is about ninety monitors, and on most plans a client who wants to see their own site is either a seat or a status page you have to design. Both turn every new client into a purchase decision, which is the quiet reason agencies stop adding sites to their monitoring.
The link also answers a message rather than a requirement. It is what you send when a client asks whether it is fixed yet, and that message is the one that interrupts whatever you were doing. Sending a link once is cheaper than answering it thirty times.
There is a monthly report to go with it, sent per client and covering only their sites, in the same ten interface languages as the rest of the engine. It exists for the same reason: monitoring nobody sees is monitoring nobody pays for. src/Report.php.
Questions people ask before sending the first link
Does the client need an account?
No, and there is nothing to create on their side. The link is the whole credential: they open it and the page is there. That is deliberate, because the alternative is an invitation to chase, a password to reset on a Sunday, and a seat to pay for on most plans.
If the link is the password, what happens when it leaks?
You rotate it, from the client screen, and the old one stops working on the next request. Nothing is lost: the history, the sites and the monitors are attached to the client record, not to the token. There is also a switch that closes the space while keeping the link, for a client whose contract is paused.
Can a client change the URL and see somebody else's sites?
There is nothing to change. The page never reads an identifier from the address bar: the token resolves to one client record, and every read is filtered by that record's id in the SQL itself. An unknown, malformed or disabled token gets the same 404, so the link cannot be used to find out which tokens exist.
Will the client page turn up in a search engine?
It sends X-Robots-Tag: noindex, nofollow, noarchive, Referrer-Policy: no-referrer and Cache-Control: private, no-store. The referrer header matters more than the first one here: without it, every outbound click from that page would hand the token to the site being linked to, and it would land in somebody else's access log.
Is this in the free engine or only in the hosted plan?
In the engine, which is MIT licensed and the same code either way. There is no paid edition and no feature flag: if you install it yourself, client links work on the first run. The hosted plan sells the server and the upkeep, not a different program.
Send one to a client this week
Or check one of their sites right now, without an account, and see what the status code was covering for.