Browser tests on Netlify deploy previews
Run plain-English browser tests against every Netlify deploy preview. Pass the preview URL from CI or a deploy-succeeded function and get evidence per run.
Deployments · Checked 2026-11-12
Netlify builds a Deploy Preview for each pull or merge request, on a URL that is not your production host. Monito runs your saved tests against that URL, keeps the runs in one project, and returns pass or fail with browser evidence.
How it works
- Netlify builds a Deploy Preview when you open a pull or merge request whose base branch is the production branch or has branch deploys enabled. The default URL is https://deploy-preview-<pull request number>--<site name>.netlify.app. While the first deploy is pending, that URL returns a Not Found error.
- A GitHub Actions job or a deploySucceeded function passes that URL to Monito. project run --base-url and the webhook's baseUrlOverride replace the origin and keep each scenario path. monito check and MCP check take the existing project ID so every preview stays in that project.
- The Actions job waits for the CLI and exits nonzero when a scenario fails or errors. The webhook returns 202 Accepted when the runs are queued. The verdict and browser evidence land in Monito either way.
Set it up
Creating the API key or the deploy webhook requires Pro or Team. A signed-in monito check or MCP check uses credits and does not require a subscription. Browser runs use credits on every path. Plan details
- Create a Monito project and save the scenarios you want to run. Create an environment named Preview for settings that do not depend on a saved login. On Pro or Team, create an API key, store it as MONITO_TOKEN, and copy the project ID.
- For GitHub Actions, add the workflow below. Replace project_123 with your project ID and mysitename with the site's Netlify subdomain (SITE_NAME). The job skips pull requests from a fork. It retries the preview URL for about 10 minutes, then runs the saved tests.
- For the deploy webhook, open the project's Automations, then Deploy webhook. Generate the secret. Store MONITO_WEBHOOK_URL and MONITO_WEBHOOK_SECRET in the Netlify UI, CLI, or API as site environment variables available to functions. Variables set in netlify.toml are not available to functions. Add the function below and deploy.
- For one preview, run the check command below, or call MCP check with url, expectation, and projectId.
name: Monito Netlify preview
on:
pull_request:
permissions:
contents: read
jobs:
monito:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 30
env:
MONITO_TOKEN: ${{ secrets.MONITO_TOKEN }}
MONITO_PROJECT_ID: project_123
PREVIEW_URL: "https://deploy-preview-${{ github.event.pull_request.number }}--mysitename.netlify.app"
steps:
- uses: actions/setup-node@v6
with:
node-version: 24
- name: Wait for the Deploy Preview
run: |
for _ in $(seq 1 60); do
if curl -fsS -o /dev/null "$PREVIEW_URL"; then
exit 0
fi
sleep 10
done
echo "Deploy Preview did not return a successful response" >&2
exit 1
- name: Run saved tests
run: npx -y @monitodev/cli@4 project run "$MONITO_PROJECT_ID" --wait --base-url "$PREVIEW_URL" --env Preview
Example our team has not run on a Netlify site. Replace project_123 and mysitename. The URL is Netlify's default Deploy Preview host. The loop gives the first deploy about 10 minutes to leave the documented Not Found error. A later push can get a successful response from the previous preview, because that URL stays the same until the new deploy replaces it. Netlify's docs describe GitHub commit statuses, commit checks, and pull request comments. They do not describe a deployment_status event, so this job does not read one.
Call the deploy webhook from deploySucceeded
Netlify's deploySucceeded handler runs when a deploy finishes. The event's deploy.sslUrl field is the HTTPS URL of the deploy, deploy.url is the primary URL, and deploy.permalinkUrl is the permalink. The sample sends sslUrl, and only when deploy.context is deploy-preview, so a production or branch deploy does not call Monito. There is no native Netlify webhook in Monito. This uses the generic deploy webhook.
Netlify's outgoing HTTP Post Request notification sends Netlify's event JSON. If you set a JWS secret, Netlify adds X-Webhook-Signature. The docs do not describe a custom Authorization header, so do not point that notification at the Monito webhook. Build hooks are incoming URLs that start a Netlify build. They do not call Monito when the deploy finishes.
On Pro and Enterprise, a variable's scope must include Functions before a function can read it. Changes apply on the next deploy. If a public site's untrusted deploys policy builds fork previews without sensitive variables, a name such as MONITO_WEBHOOK_SECRET is not passed to that preview. An automatic deploy subdomain changes DEPLOY_PRIME_URL, so the default netlify.app host in the workflow is then the wrong host. One documented custom host is deploy-preview-42.early-access.company.com.
import type { DeploySucceededEvent } from "@netlify/functions";
export default {
async deploySucceeded(event: DeploySucceededEvent) {
if (event.deploy.context !== "deploy-preview") return;
const previewUrl = event.deploy.sslUrl;
const webhookUrl = process.env.MONITO_WEBHOOK_URL;
const webhookSecret = process.env.MONITO_WEBHOOK_SECRET;
if (!previewUrl || !webhookUrl || !webhookSecret) {
throw new Error("Missing the preview URL or Monito webhook variables");
}
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
Authorization: "Bearer " + webhookSecret,
"Content-Type": "application/json",
},
body: JSON.stringify({
baseUrlOverride: previewUrl,
env: "Preview",
}),
});
if (!response.ok) {
throw new Error("Monito webhook returned " + response.status);
}
},
};
Typed handler from Netlify's event docs. Our team has not run this function on Netlify. HTTP 202 means the runs were queued. The function does not wait for the verdict, and it does not hold the deploy open. Saving the Vercel webhook on the same Monito project replaces this bearer secret.
Check one preview from the CLI or MCP
monito check takes the preview URL, the expected behavior, and --project. In MCP, call check with url, expectation, and projectId. Without a project ID, a new hostname can land in a new project. Agent-run Deploy Previews use a different prefix, agent- plus the run ID. The workflow above builds only the pull-request URL.
PREVIEW_URL="https://deploy-preview-42--mysitename.netlify.app" monito check "$PREVIEW_URL" "The cart total includes shipping" --project project_123 --json
Replace the URL and project_123. The CLI flag is --project. The MCP argument is projectId.
Netlify sources
Netlify behavior on this page was checked against the following docs on 2026-11-12. The benchmark reports below do not validate this workflow or this function. Our team has not run either on a Netlify site.
Example result
Example result: field names from the CLI, values illustrative
A separate CLI check --json can diagnose a deployed journey with these fields. This is a browser-verdict example, not the webhook response and not the project run response: HTTP 202 returns queued IDs and a results URL before the tests finish. project run --wait --json returns a runs array.
{
"checkId": "check_example",
"projectId": "project_example",
"runId": "run_example",
"done": true,
"status": "failed",
"cause": "product_defect",
"causeLabel": "App bug",
"durationMs": null,
"creditsCharged": null,
"reportUrl": "https://www.monito.dev/session/session_example",
"firstFailure": "The cart total does not include shipping."
}Limits to account for
A base-URL override replaces the target origin only. It keeps the scenario path, query, and fragment. It does not approve a saved login on a new origin.
Saved logins are bound to an exact HTTPS origin. A Deploy Preview hostname is a different origin. Selecting the Preview environment does not extend that approval. A test that needs a saved login fails closed and still uses credits. Use a flow that works without a saved login.
202 Accepted means runs were queued. It does not mean tests passed, and it does not hold the deploy open. The GitHub Actions job passes --wait, so that job can fail. Creating the API key or the deploy webhook requires Pro or Team. Browser runs use credits.
Password Protection on Deploy Previews, including a shared password and the password option under project visibility, shows a password prompt instead of the app. Team login and a private project show a Netlify login. A Basic-Auth header on a path asks the browser for a password too. Monito does not enter that password, and this page has no bypass.
The GitHub Actions URL is the default netlify.app host. It can respond with the previous preview on a later push to the same pull request. deploySucceeded runs after the deploy that just finished. Netlify does not document a GitHub deployment_status payload for this, so the job does not read github.event.deployment_status.
There is one deploy webhook per Monito project. Saving the Vercel webhook replaces the generic webhook and invalidates its bearer secret.
Comparable benchmark runs
Our team published these runs in the reliability benchmark. They show comparable browser journeys, not executions of the configuration or illustrative result above. Run your own scenario before relying on this integration.
Questions
Can I run tests on every Netlify deploy preview?
Netlify builds a Deploy Preview for each pull or merge request when the base branch is the production branch or has branch deploys enabled. That is the default. Pass the preview URL to project run, or call monito check with the existing project ID. The sample function runs only when deploy.context is deploy-preview. The GitHub Actions example builds the default pull-request URL and can hit the previous preview on a later push. Agent-run previews use an agent- URL, which that workflow does not construct. A password-protected preview blocks the browser.
Do I need Playwright for this?
No. This page does not install or run Playwright. You describe the expected behavior in plain English, and Monito runs that check in a browser. A Playwright suite you already have is separate. This setup does not configure it.
Can tests log in on a deploy preview?
Saved logins are approved for an exact HTTPS origin. A Deploy Preview hostname is a new origin, and naming the Monito environment Preview does not approve it. A test that needs a saved login fails closed and still uses credits. Use a flow that works without a saved login, or approve one stable origin instead of every preview URL.
Does it work with Netlify's password-protected previews?
No. A shared password on Deploy Previews, and the password option under project visibility, shows a password prompt instead of the app. Team login and a private project show a Netlify login. Monito's browser does not enter that password or complete that login, and this page has no bypass. Test a preview that visitors can open without that prompt.
Pricing
Plans are Free, Pro at $79/mo, Team at $199/mo, and Managed at $750/mo.