Synthetic monitoring vs uptime checks: what each one actually proves
An uptime check proves your homepage answers. Synthetic monitoring proves a browser could finish a task. Neither proves a customer could sign up and pay, unless you point it at that path.
Picture the 2 a.m. page that never came. Your status page has been green for a month. Every check from every region says 200 OK in under a hundred milliseconds. And on Tuesday a customer emails to say the upgrade button has been broken since Friday, so they gave up and went with someone else.
Nothing lied. The homepage really was up. The question you were asking was simply too small. This post is about the three sizes of question a monitor can ask, what each one proves, and why the difference costs money on a SaaS specifically.
What an uptime check proves
An uptime check is an HTTP request on a timer. Every thirty or sixty seconds, a server somewhere fetches a URL and records three things: whether the connection succeeded, what status code came back, and how long it took. Better ones add a string match on the body, a certificate expiry check, and probes from several regions so a single bad network route does not page you.
That is genuinely useful. It catches DNS mistakes, expired TLS certificates, a crashed process, a full disk, a bad deploy that takes the whole app down, and a provider outage. It is cheap, it runs constantly, and its alerts are easy to trust because a 503 is a 503.
Here is the limit, and it is worth saying plainly: an uptime check proves that one URL returned one status code. It does not execute your JavaScript, so a broken bundle that leaves the page blank still returns 200. It does not fill in forms. It does not receive email. It does not follow a redirect to Stripe. It does not know your app has a paid area, let alone whether the right people can get into it.
What synthetic monitoring proves
Synthetic monitoring replaces the HTTP request with a scripted browser. Instead of asking “did the URL answer?”, it asks “could a browser complete these steps?” A script opens the page, waits for it to render, clicks, types, and asserts that something appeared. Because it is a real browser, it sees what a user sees: the JavaScript executed, the API calls the page made, the console errors, the element that never showed up.
The word synthetic just means the traffic is generated by the monitor rather than by real users. The alternative is real-user monitoring, which watches what actual visitors experience but can only tell you about a path after someone has walked it and suffered. Synthetic checks walk the path before anyone else does, which is the whole point.
So a synthetic check proves more. It catches the blank page, the login form that posts to an endpoint that now 500s, the search that returns nothing, the third-party widget that stopped loading. Whatever you script, it verifies.
And that sentence contains the catch. It verifies whatever you script. Most teams script the homepage and the login page, because those are easy, and stop there. The check turns green, the team relaxes, and the paths that actually make money go unwatched.
Where generic synthetic checks stop
Consider what a new customer on a SaaS actually does. They sign up with an email address. They wait for a welcome or magic-link email and click it. They hit an upgrade button and land on Stripe Checkout. They pay, and something in your app unlocks. Four steps, and each one crosses a boundary that a page-level script does not cross on its own.
- Signup needs a fresh identity. Reusing one test account tests the login path, not the signup path. Your signup form, your rate limits and your welcome-email trigger only run for new accounts.
- The email step needs an inbox. A browser cannot receive mail. To prove the welcome email arrived and the link works, the monitor has to own an address, wait for the message, and follow the link from inside it.
- The payment step needs a plan for not paying. Stripe test cards are declined on live keys. A script that pays for real costs money and fills your dashboard with refunds. A script that skips checkout proves nothing about the hand-off to Stripe.
- The access step needs to know what “paid” looks like. Which element only paying accounts see, or which JSON field flips to true. Nothing generic can guess that.
None of this is impossible with a general synthetic tool. It is just work that most teams never do, because it means wiring an inbox, handling throwaway accounts, deciding how to prove payment safely, and keeping selectors current every time the pricing page changes. The checks that exist are the easy ones, and the easy ones are not where the revenue is.
The path worth checking: signup to paid access
Call the four steps above the revenue path. For a SaaS, it is the only path that turns a visitor into an invoice, and it is the path most likely to break quietly. Signup breaks when a validation rule changes. Email breaks when a provider key rotates or a domain record lapses. Checkout breaks when a price id is missing from a new environment. Access breaks when a webhook handler is refactored. None of those produce a 503 on the homepage.
A revenue-path check is a synthetic check pointed at exactly those four steps, with the plumbing built in: a fresh account per run, its own inbox, a way to reach Stripe Checkout and stop, and a definition of what paid access looks like. It is what a run is in Mystra, and it is also something you can build yourself with Playwright, a catch-all mailbox and a cron job, if you have the time for it.
How to choose, and how to combine them
You do not pick one. The three sizes of question answer different things at different costs, and the sensible setup layers them.
- Keep the uptime check. It is nearly free, it runs every thirty seconds, and it is the fastest way to learn the whole site is down. Point it at the homepage and at one API endpoint that touches the database.
- Add one synthetic check per path that makes money. For most SaaS products that is one path. Do not script twenty pages; script the four steps a paying customer takes, and script them from a brand-new account.
- Run it on deploy, not only on a schedule. The deploy is the moment things break. A check that runs every hour finds the problem up to sixty minutes late; a check that runs from a deploy webhook finds it while the commit is still on your screen.
- Alert only on proof. A browser script has many ways to be flaky: a slow third party, a cookie banner, a selector that moved. If every timeout pages someone, the alerts get muted within a week. Treat a 5xx, a missing email or a paid area that stayed locked as broken. Treat “could not find the button” as a question, not an alarm.
Where Mystra fits, honestly
Mystra is the third row of that figure and nothing else. It is not an uptime monitor and does not try to be; keep the one you have. What it does is walk the revenue path in a real Chrome browser after every deploy: sign up with a throwaway address at its own inbox domain, receive and follow the welcome email, press your upgrade button until Stripe Checkout appears, read the plan and amount, stop without paying, and confirm the paid area is still closed to that unpaid account. Each run keeps a screenshot per step, a video, a HAR network capture and the console log, and an alert only goes out when a step broke with proof. The details of the payment step are in Payment proof, and the manual version of the same check is in How to test a Stripe Checkout flow without paying.
Takeaways
- An uptime check proves a URL answered. A synthetic check proves a browser could finish the steps you scripted. Neither proves a customer could pay unless you script that path.
- The revenue path on a SaaS is signup, welcome email, checkout and paid access. It needs a fresh account, an inbox, a safe way to reach Checkout and a definition of paid.
- Layer them: uptime every thirty seconds, the revenue path on every deploy, alerts only with proof.
See this run on your own app
Mystra walks signup, welcome email, checkout and paid access after every deploy, with proof. One app and 25 runs a month are free, no card.
Keep reading
Guide9 min read
How to test a magic-link signup flow end to end
A magic-link signup has five hops and fails silently at each one. Here is where it breaks, how to test it by hand, in CI, and on production after every deploy.
Read the post →