- starter: the application without the ShieldLabs integration.
- final: the same application with browser identification and a server-owned decision.
starter first to see the unprotected action, then follow its guide through the changes in final. Each finished app sends a request ID from the browser to its server, which reads the identification and makes the decision. You can run one folder without installing the other tutorials.
Choose an application
Run starter
You need Node.js 22 or later and npm. Clone the public tutorial repository, then run one application:new-account-fraud with the chosen folder. No ShieldLabs key is required on starter. Use only invented details or the supplied demo credentials.
Add ShieldLabs
The finished app needs a Public Key in its browser and a Private API Key on its server. Add the hostname under Integration > Domains and copy its matching keys from Integration > API keys. Adding a ShieldLabs domain does not create DNS or host your app: point that hostname at your own server, serve it over HTTPS and route requests to the Node process on127.0.0.1:3000. The starter works locally; a customer’s key is not automatically accepted from localhost. Without an HTTPS host, you can still follow the code and run the local tests, but you cannot complete a live final identification.
1
1. Compare the two versions
Stop the starter server. From the repository root, compare the app you chose with its completed version. For example:Use
origin/final here: a fresh clone has the remote branch, but does not have a local final branch yet. The guide for each scenario names the browser, server and decision files to inspect.2
2. Follow the request ID from browser to server
Each The Public Key is visible to the browser. The Private API Key is not sent with the form.
final app uses @shieldlabs-ai/js in its browser helper. A protected action sends its fresh requestId to the app server. For example, Account sharing starts a check when the login form receives focus:3
3. Verify and make the decision on the server
Each example’s The business rule is in that scenario’s own server module. It uses a verified Device ID, account or Risk Score as needed; the server does not trust a risk result sent by the browser.
server/shieldlabs.js uses @shieldlabs-ai/node and the Private API Key to read History by that request ID. It refuses missing, stale, reused or unusable results before the scenario’s own rule runs. In the Account sharing server, the login flow begins by checking the ID:4
4. Run the completed version
Switch to Continuing the new-account-fraud example:Your ignored
final and reinstall dependencies in the chosen folder. The ignored .env from starter contains no key entries; add these two lines with the matching values:.env is not replaced by a branch switch. Keep your own uncommitted work in another clone before switching. Open the completed app through your registered HTTPS hostname; npm start runs its Node server on loopback behind your reverse proxy.5
5. Check the result
Perform the actions in your scenario’s guide. Compare the request ID and Device ID with the matching identification in History. Check the decision on screen and the state after repeating the action. Use invented personal details; purchases, messages, rewards and challenges are simulated.
observed_at as a precaution against early updates. This delay is not an explicit finality marker or a guarantee that the row cannot change later.
Test and reset
From each app folder, runnpm run check and npm test. The tests use an isolated database and synthetic History responses; they do not call live scoring or send real payments/messages. The optional test-bot.js requires dev dependencies (install with npm ci) and a separately configured browser-test environment.
DEMO_ALLOW_RESET=1 permits resetting only the disposable demo’s database. Schema differences between starter and final can recreate that database when you switch versions; do not store anything important there.
For a live check, record the real request IDs and corresponding History rows. Avoid unnecessary parallel identification runs. If the service rate-limits you, stop and check your account limits before retrying. A private window alone does not establish a new or identical Device ID: compare the observations.
If a check does not finish
- Final integration is not configured: replace the placeholders in the private
.envwith the domain’s actual Public Key and Private API Key. Restart the server and reload the page. The browser receives only a setup boolean and the Public Key, never the Private API Key. - The identification could not be verified: an initialized request ID does not by itself prove that the snapshot was accepted and stored. Check the browser network/console and look for that same request ID in History. Do not substitute a fabricated successful identification.
- HTTP 429 on challenge or snapshot: stop live checks and automatic retries. Respect the service’s retry guidance and verify the IP/domain/plan context before trying again. A 429 is not by itself proof of a specific ban reason. Do not switch IPs or change headers to evade a limit.
- Cookies from another tutorial affect sign-in: the apps namespace their own demo-session cookies. The scoring snippet’s cookies are separate. Restarting or switching versions can clear that app’s disposable database, so use its reset/login flow rather than deleting all browser cookies repeatedly.