NewIntroducing semantic snapshotsPair every capture with structured DOM data →

For flows that must keep working

Walk through a signup flow and fail loudly when a step breaks

Describe the steps a user takes and get a pass or fail for each one from a real browser, with a clear error when one breaks.

The problem

The signup form still renders, but its button now posts to an endpoint that was renamed, and nobody finds out until the sign-up chart flattens. An end-to-end suite catches that in CI, but not when a third-party script, a CMS edit or a configuration change touches production afterwards.

How domscout handles it

actions runs up to 25 steps in order: type into a field found by its label, click a button found by its role and name, wait for the URL to change or for text to appear, and assert what should now be true. Each step reports passed or failed in actionResults. A failed assert ends the request with 422 ASSERTION_FAILED, a target that matches nothing with 404 LOCATOR_NOT_FOUND, and one that matches several with 409 LOCATOR_AMBIGUOUS, so a broken flow cannot pass quietly. Monitors do not run actions, so schedule the check from CI or your own cron, and point it at a test account.

Where it shows up

  • A post-deploy check that signup still reaches its confirmation page.
  • A scheduled walk through checkout in your payment provider's test mode.
  • A check that a form's validation messages still appear.

The request

POST /screenshot. Locator targets, waits and assertions start at Pro. A run costs 1 credit, plus 1 past 10 steps. The steps share a 24-second budget in a direct request and 52 seconds in a job; any still pending when it runs out are reported as skipped, with a 206.

cURL
curl -X POST https://api.domscout.io/screenshot \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://staging.example.com/signup",
    "responseType": "json",
    "actions": [
      {
        "action": "type",
        "target": {"label": "Email"},
        "text": "qa+signup@example.com"
      },
      {
        "action": "click",
        "target": {"role": {"role": "button", "name": "Create account"}}
      },
      {"action": "waitForUrl", "url": "/welcome"},
      {
        "action": "assert",
        "target": {"role": {"role": "heading", "name": "Check your inbox"}},
        "expected": "visible"
      }
    ]
  }'
Node.js
const response = await fetch("https://api.domscout.io/screenshot", {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": "YOUR_API_KEY" },
  body: JSON.stringify({
    url: "https://staging.example.com/signup",
    responseType: "json",
    actions: [
      {
        action: "type",
        target: { label: "Email" },
        text: "qa+signup@example.com"
      },
      {
        action: "click",
        target: { role: { role: "button", name: "Create account" } }
      },
      { action: "waitForUrl", url: "/welcome" },
      {
        action: "assert",
        target: { role: { role: "heading", name: "Check your inbox" } },
        expected: "visible"
      }
    ]
  }),
});
const result = await response.json();
console.log(result.actionResults);
Python
import requests

response = requests.post(
    "https://api.domscout.io/screenshot",
    headers={"x-api-key": "YOUR_API_KEY"},
    json={
        "url": "https://staging.example.com/signup",
        "responseType": "json",
        "actions": [
            {
                "action": "type",
                "target": {"label": "Email"},
                "text": "qa+signup@example.com"
            },
            {
                "action": "click",
                "target": {"role": {"role": "button", "name": "Create account"}}
            },
            {"action": "waitForUrl", "url": "/welcome"},
            {
                "action": "assert",
                "target": {
                    "role": {"role": "heading", "name": "Check your inbox"}
                },
                "expected": "visible"
            }
        ]
    },
)
print(response.json()["actionResults"])
Go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"url": "https://staging.example.com/signup",
		"responseType": "json",
		"actions": []any{
			map[string]any{
				"action": "type",
				"target": map[string]any{"label": "Email"},
				"text": "qa+signup@example.com",
			},
			map[string]any{
				"action": "click",
				"target": map[string]any{
					"role": map[string]any{"role": "button", "name": "Create account"},
				},
			},
			map[string]any{"action": "waitForUrl", "url": "/welcome"},
			map[string]any{
				"action": "assert",
				"target": map[string]any{
					"role": map[string]any{"role": "heading", "name": "Check your inbox"},
				},
				"expected": "visible",
			},
		},
	})
	req, _ := http.NewRequest("POST", "https://api.domscout.io/screenshot", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", "YOUR_API_KEY")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var result map[string]any
	json.NewDecoder(res.Body).Decode(&result)
	fmt.Println(result["actionResults"])
}
PHP
<?php
$ch = curl_init('https://api.domscout.io/screenshot');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'x-api-key: YOUR_API_KEY'],
    CURLOPT_POSTFIELDS => json_encode([
        'url' => 'https://staging.example.com/signup',
        'responseType' => 'json',
        'actions' => [
            [
                'action' => 'type',
                'target' => ['label' => 'Email'],
                'text' => 'qa+signup@example.com'
            ],
            [
                'action' => 'click',
                'target' => [
                    'role' => ['role' => 'button', 'name' => 'Create account']
                ]
            ],
            ['action' => 'waitForUrl', 'url' => '/welcome'],
            [
                'action' => 'assert',
                'target' => [
                    'role' => ['role' => 'heading', 'name' => 'Check your inbox']
                ],
                'expected' => 'visible'
            ]
        ]
    ]),
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
$result = json_decode($response, true);
print_r($result['actionResults']);
Ruby
require "json"
require "net/http"

response = Net::HTTP.post(
  URI("https://api.domscout.io/screenshot"),
  {
    url: "https://staging.example.com/signup",
    responseType: "json",
    actions: [
      {
        action: "type",
        target: { label: "Email" },
        text: "qa+signup@example.com"
      },
      {
        action: "click",
        target: { role: { role: "button", name: "Create account" } }
      },
      { action: "waitForUrl", url: "/welcome" },
      {
        action: "assert",
        target: { role: { role: "heading", name: "Check your inbox" } },
        expected: "visible"
      }
    ]
  }.to_json,
  "Content-Type" => "application/json",
  "x-api-key" => "YOUR_API_KEY"
)
puts JSON.parse(response.body)["actionResults"]
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Capture {
    public static void main(String[] args) throws Exception {
        String body = """
            {
                "url": "https://staging.example.com/signup",
                "responseType": "json",
                "actions": [
                    {
                        "action": "type",
                        "target": {"label": "Email"},
                        "text": "qa+signup@example.com"
                    },
                    {
                        "action": "click",
                        "target": {
                            "role": {"role": "button", "name": "Create account"}
                        }
                    },
                    {"action": "waitForUrl", "url": "/welcome"},
                    {
                        "action": "assert",
                        "target": {
                            "role": {
                                "role": "heading",
                                "name": "Check your inbox"
                            }
                        },
                        "expected": "visible"
                    }
                ]
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.domscout.io/screenshot"))
            .header("Content-Type", "application/json")
            .header("x-api-key", "YOUR_API_KEY")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
C#
using System.Net.Http.Json;
using System.Text.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");

var response = await client.PostAsJsonAsync("https://api.domscout.io/screenshot", new {
    url = "https://staging.example.com/signup",
    responseType = "json",
    actions = new[] {
        new {
            action = "type",
            target = new { label = "Email" },
            text = "qa+signup@example.com"
        },
        new {
            action = "click",
            target = new {
                role = new { role = "button", name = "Create account" }
            }
        },
        new { action = "waitForUrl", url = "/welcome" },
        new {
            action = "assert",
            target = new {
                role = new { role = "heading", name = "Check your inbox" }
            },
            expected = "visible"
        }
    }
});
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(result.GetProperty("actionResults"));

Go further

More in Testing and QA: Responsive layout checks · Visual regression testing · JavaScript error checks · Hover, focus and dark mode screenshots · Localization screenshots · Unlabelled control checks · A screenshot of every step · Embedded widget checks · Screenshots for support tickets · Ad landing page checks · Before and after hydration · Selectors for test automation · Print layout checks · All use cases

Give your product a browser.

Get clean web content and visual proof into your workflow in minutes.