Developer docs
Widget reference
The widget is one script tag on your staging site. It stays invisible to ordinary visitors, wakes only for an invited tester, and mounts inside a closed Shadow DOM so its styles cannot leak into your page and your page's scripts cannot reach into it. Every request it makes is outbound from the tester's browser to DeploySeal — DeploySeal never connects to your staging network, so private, VPN-gated and password-protected environments work unchanged.
Install
Paste the snippet from Sites → your site → Install widget before the closing </body> tag. It loads asynchronously and never blocks rendering. Once the first heartbeat arrives the site shows the widget as Active and testers can be invited.
<script src="https://cdn.deployseal.com/ds-widget.js" data-ds-site-key="ds_pub_••••••••••••••••" data-ds-api-base="https://api.deployseal.com" data-ds-environment="staging" data-ds-build="2026.09.04-a1b2c3d" async ></script>
Attributes
| Attribute | What it does |
|---|---|
| data-ds-site-keyrequired | The public identifier of the site (or of one of its environments) the snippet belongs to. Copy it from Sites → your site → Install widget. It is not a secret: it only works from the site's allowed origins, so a copied snippet is useless anywhere else. Without this attribute the widget logs a warning and does nothing. |
| data-ds-api-baseoptional | The DeploySeal API the widget talks to. Defaults to https://api.deployseal.com; the snippet the dashboard generates always includes the right value for your deployment. |
| data-ds-environmentoptional | A label for the deployment the snippet is installed on (staging, uat, …). Defaults to staging. Environments with their own key and origin list are managed under the site's Environments section. |
| data-ds-buildoptional | Your build identifier for the deployed code — a version, a git SHA, a CI run number — up to 64 characters. The widget sends it with every page visit, and the readiness report compares it with the build the campaign expected. Without a marker the report says Not verified by build marker rather than guessing. A <meta name="ds-build"> tag is read live as an alternative (the script attribute wins when both are present). |
Build marker via meta tag
<!-- Alternative to data-ds-build: read live on every page, so a
single-page app can update it after each deploy -->
<meta name="ds-build" content="2026.09.04-a1b2c3d">Optional markup on your own elements
<!-- Optional: a stable, deploy-proof anchor for element pins --> <button data-ds-id="checkout-pay-now">Pay now</button> <!-- Optional: mask the text of an element in session replays --> <p data-ds-mask>Order for Jane Citizen, 14 Example St</p> <p class="ds-mask">…also works as a class</p>
Allowed origins
Every widget request carries the browser's Origin header, and the API accepts it only if it exactly matches one of the site's allowed origins — scheme, host and port, compared case-insensitively, e.g. https://staging.yourstore.co.za. Requests from any other origin are rejected, which is what makes the public site key safe to ship in HTML.
- Origins are set per site under Allowed origins. An environment with its own key can carry its own list; when that list is empty the site's list applies.
- In production a site with no allowed origins rejects every widget call — the dashboard warns you until you add one.
- Regenerating an environment key invalidates the old snippet immediately.
How a tester session starts
- A tester is invited to a campaign and receives a personalised link. Accepting it sends them to your staging URL with ?ds_cti=… appended — the campaign-tester id that is their credential. No DeploySeal account is involved.
- The widget reads the parameter, stores it in sessionStorage (so it survives navigation within the tab), strips it from the address bar, and calls the bootstrap endpoint with the site key and that id.
- Bootstrap succeeds only when the origin is allowed, the tester belongs to the campaign, and the campaign is active. Then the bar appears — across the top on desktop, along the bottom on a phone — with the campaign's instructions, the task list with each task's expected outcome, pins already on this page, and the tester's verdict controls. Session recording starts at this point and not before.
- On any other page load — no id, an unknown id, a closed campaign, a disallowed origin — the widget sends the install heartbeat and stays hidden (display:none).
Continue on a phone. The tester's dashboard, the widget and the invitation email can show a QR code that encodes a one-time handoff link (/t/<code>). Scanning it on a phone redeems the code — valid once, for ten minutes — and opens the staging site with the same tester session attached, so mobile tasks are recorded against the same person.
After the seal. When the readiness report is sealed, task results and verdicts are frozen in the widget until the record is reopened; issues can still be filed against the campaign.
What the widget records
Nothing below leaves the browser without an active tester session, except the install heartbeat. URLs pass through a sanitiser that strips tokens, one-time codes and email addresses before they are sent.
Install heartbeat
Every page load with the snippet installed
Site key, the page URL (sanitised) and title. This is how the dashboard shows the widget as Active and lets you invite testers. It is the only thing sent when no tester session is present.
Page-visit coverage
Active tester session; on load and on every SPA route change
Page URL and title, user agent, and the build marker. The report's page-coverage, device-matrix and effort sections are built from these pings.
Session replay
Active tester session; attached to issues the tester pins
A rolling 60-second window of DOM changes and interactions (rrweb), kept in memory and trimmed to at most 8 MB. When an issue is submitted, the clip leading up to the pin is uploaded with it. Every input value is masked by default — emails, names, card numbers, not just passwords — and any element marked data-ds-mask or .ds-mask has its text masked too.
Annotated screenshot
When the tester pins an issue
A snapshot of the visible viewport at up to 2× device pixel ratio, rendered from the DOM by the browser itself (modern CSS included) with the widget's own UI excluded. The tester can draw boxes and arrows, and redact regions; redactions are burned into the pixels before upload, so the original never leaves the browser.
Element anchor
When the tester pins an issue
Where on the page the pin landed: a ranked set of candidates (a data-ds-id if you set one, then structural selectors, text and position) so the pin can be re-placed on later builds with a confidence score, plus a drafted title from the element it points at.
Ambient context
When the tester submits an issue — shown first as removable chips
The last 50 console errors and warnings, uncaught exceptions and unhandled rejections; the last 25 failed network requests (non-2xx, or a network-level failure) with method, URL, status and duration; and the page's web vitals (LCP, CLS, INP). The widget's own API traffic is never recorded. The tester sees exactly what is attached and can remove any of it before submitting.
Device context
With every issue and task result
Browser and version, operating system, screen size, and the page URL and title. Task results recorded on a phone are marked as such, which is what a Phone-only task checks.
Every uploaded artifact is fingerprinted with SHA-256 on receipt; the fingerprint is what the sealed record and the evidence package carry, so a screenshot or replay can be checked byte-for-byte later. See Verifying a sealed record.
Content-Security-Policy
If your staging site sends a CSP, allow the widget script, the API it calls, and the font it loads inside its shadow root (DM Sans from Bunny Fonts — no Google Fonts, no tracking). Use the hosts from your own install snippet if your deployment is self-hosted.
Content-Security-Policy: script-src … https://cdn.deployseal.com; connect-src … https://api.deployseal.com; style-src … https://fonts.bunny.net; font-src … https://fonts.bunny.net;
Screenshots are rendered from the DOM in the browser, so no img-src change is needed for capture; replay and screenshot uploads go to the API host above.
Ready to install? The snippet with your key lives on the site page.
Open your sitesBuilding on top? See the API reference and the webhook events reference.