Docs menu
Guides

Integrate the browser script

inlineshield.js screens the sign-up from the player's browser and hands your server a single-use token. Your server redeems the token with POST /api/v1/siteverify and gets the decision.

Last updated

Add the tag

Add the script to the page with your sign-up form, with a site key from Settings › Keys. The page's domain must be on the site key's list of domains: the check is accepted only from a browser Origin on that list.

HTML
<script src="https://api.inlineshield.io/inlineshield.js" data-sitekey="YOUR_SITE_KEY" async></script>

What the script does

  1. It watches form submissions on the page. A form takes part when it has an email field (type="email", or a name or id containing email).
  2. On submit it sends the email to POST /api/v1/check with your site key, the time the page was open, a username field if there is one, a hidden honeypot field if you have one (named honeypot, website or phone_check), and browser signals.
  3. If the answer is BLOCKED, the form is not submitted and the first reason is shown under the email field.
  4. Otherwise it puts the token in a hidden field named betshield_token and submits the form.
  5. If the check fails or takes longer than 3 seconds, the form is submitted without a token. The script never blocks a real player because of a network or setup problem.

Redeem the token on your server

Before you create the account, send betshield_token and the submitted email to POST /api/v1/siteverify with your secret key in X-Api-Key. A token works once, for 10 minutes, for the same email, and only with a secret key of the same account. JavaScript-submitted forms: include `form.betshield_token.value` in your request, or send `new FormData(form)`.

# $TOKEN is the betshield_token form field, $EMAIL the submitted email
curl -X POST https://api.inlineshield.io/api/v1/siteverify \
  -H "X-Api-Key: $INLINESHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\": \"$TOKEN\", \"email\": \"$EMAIL\"}"

# Switch on action: "allow" → create the account; "hold" → create it pending and store reviewId; "block" → reject the signup.
# A "hold" answer also carries a reviewId (rev_...): keep it, the signup is resolved later by a
# webhook (signup.approved / signup.rejected) or by GET https://api.inlineshield.io/api/v1/reviews/<reviewId>.

# If the answer is {"success": false, "error": "NO_TOKEN"} (script blocked, timed out or
# missing), judge the signup server-side instead, with the visitor's IP and User-Agent
# taken from YOUR incoming request, and switch on "action" the same way
# (strict mode, REQUIRE_INLINESHIELD_TOKEN = true in the other snippets, rejects instead):
curl -X POST https://api.inlineshield.io/api/v1/verify \
  -H "X-Api-Key: $INLINESHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"email\": \"$EMAIL\", \"ip\": \"$VISITOR_IP\", \"userAgent\": \"$VISITOR_UA\"}"

# HTTP 401 (bad API key) means reject. HTTP 402 {"error": "QUOTA_EXCEEDED", ...} means the
# account is over its monthly check limit: treat it like "InlineShield unavailable", exactly as
# for a timeout, 5xx, any other non-2xx answer, non-JSON, or a /verify answer with no
# "decision" field: accept the signup and log it (INLINESHIELD_FAIL_CLOSED = true in the other
# snippets rejects instead).

Field by field: POST /api/v1/siteverify.

When the form arrives without a token

/siteverify answers {"success": false, "error": "NO_TOKEN"}. The script may have been blocked by an extension, timed out, or not loaded. If error is "NO_TOKEN" (the script was blocked, timed out or is missing), the snippet judges the signup server-side with POST /api/v1/verify (email, visitor IP and User-Agent) and applies the same rule to its decision; set REQUIRE_INLINESHIELD_TOKEN = true to reject those signups instead. If InlineShield itself is unreachable, times out or errors, the snippet accepts the signup and logs a warning; set INLINESHIELD_FAIL_CLOSED = true to reject instead. While your account is over its monthly limit, signups arrive without a token and InlineShield answers QUOTA_EXCEEDED, which follows INLINESHIELD_FAIL_CLOSED even in strict mode.

What /check receives

The IP address and User-Agent come from the browser's request itself, never from the body.

FieldTypeRequiredDescription
siteKeystringyesYour site key (bs_site_…, or bs_site_test_… for test mode).
emailstringyesat least 1 character
usernamestringno
clientDwellMsnumbernoMilliseconds the form was open before it was submitted.
honeypotstringnoThe value of a hidden field people leave empty.
signalsobjectnoBrowser signals collected by inlineshield.js. Invalid values are ignored.
signals.webdriverbooleanno
signals.tzstringnoIANA time zone, e.g. Europe/Berlin.at most 64 characters
signals.langsarray of stringnoUp to 5 browser languages are kept.
signals.automationHintsarray of stringnoValues: webdriver, cdc_props, no_languages, headless_ua_ch, phantom, nightmare, selenium_props