Docs menu
Guides
API reference
POST /api/v1/check
Browser check (site key). The browser script and the backend token check. Generated from the OpenAPI file.
Last updated
Overview
POST https://api.inlineshield.io/api/v1/check
Called by inlineshield.js in the player's browser. Authenticated by the site key in the body and the browser's Origin, which must be one of the site key's domains. Screens the sign-up and returns the verdict and a single-use token (valid for 10 minutes) for /siteverify.
The IP and User-Agent are taken from the request itself, never from the body. Reasons produced by learned signals are shown as one generic line here; /siteverify returns them in full.
Per-minute limits: 30 checks per client IP, and your plan's limit per account (300 for test site keys).
Authentication
No API key. The site key in the body and the browser’s Origin header identify your site; see the request fields and parameters below.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Origin | header | string | yes | Set by the browser. Must be an https or http origin on the site key's list of domains. |
Request body
JSON (Content-Type: application/json), required.
Other fields are ignored. The IP and User-Agent always come from the request.
| Field | Type | Required | Description |
|---|---|---|---|
siteKey | string | yes | Your site key (bs_site_…, or bs_site_test_… for test mode). |
email | string | yes | at least 1 character |
username | string | no | |
clientDwellMs | number | no | Milliseconds the form was open before it was submitted. |
honeypot | string | no | The value of a hidden field people leave empty. |
signals | object | no | Browser signals collected by inlineshield.js. Invalid values are ignored. |
signals.webdriver | boolean | no | |
signals.tz | string | no | IANA time zone, e.g. Europe/Berlin.at most 64 characters |
signals.langs | array of string | no | Up to 5 browser languages are kept. |
signals.automationHints | array of string | no | Values: webdriver, cdc_props, no_languages, headless_ua_ch, phantom, nightmare, selenium_props |
Request examples
{
"siteKey": "bs_site_test_9QpX2mV7cN4bL1kJ8hG5fD3sA6zW0yT",
"email": "allow@test.inlineshield.io",
"clientDwellMs": 8200
}{
"siteKey": "bs_site_test_9QpX2mV7cN4bL1kJ8hG5fD3sA6zW0yT",
"email": "nobonus@test.inlineshield.io",
"clientDwellMs": 8200
}{
"siteKey": "bs_site_test_9QpX2mV7cN4bL1kJ8hG5fD3sA6zW0yT",
"email": "review@test.inlineshield.io",
"clientDwellMs": 8200
}{
"siteKey": "bs_site_test_9QpX2mV7cN4bL1kJ8hG5fD3sA6zW0yT",
"email": "kyc@test.inlineshield.io",
"clientDwellMs": 8200
}{
"siteKey": "bs_site_test_9QpX2mV7cN4bL1kJ8hG5fD3sA6zW0yT",
"email": "block@test.inlineshield.io",
"clientDwellMs": 8200
}The first example as a cURL command:
curl -X POST https://api.inlineshield.io/api/v1/check \
-H "Origin: https://www.example.com" \
-H "Content-Type: application/json" \
-d '{
"siteKey": "bs_site_test_9QpX2mV7cN4bL1kJ8hG5fD3sA6zW0yT",
"email": "allow@test.inlineshield.io",
"clientDwellMs": 8200
}'Responses
200 OK
The verdict and a token for /siteverify. Live responses carry the quota headers; test responses do not.
Headers: X-Quota-Limit (optional), X-Quota-Used (optional), X-Quota-Reset (optional), X-Quota-State (optional).
| Field | Type | Required | Description |
|---|---|---|---|
decision | string | yes | ALLOWED: let the player in. QUARANTINED: hold for review. BLOCKED: refuse.Values: ALLOWED, QUARANTINED, BLOCKED |
riskScore | number | yes | 0 (lowest risk) to 100.0 to 100 |
reasons | array of string | yes | Plain-language reasons for the result. |
requestId | string | yes | The screening's id. Use it for feedback and support.matches ^AUD- |
token | string | yes | Single use, valid for 10 minutes: send it to /siteverify from your backend.matches ^bst_ |
{
"decision": "ALLOWED",
"riskScore": 0,
"reasons": [
"Test mode: allow@test.inlineshield.io always gives ALLOWED / allow."
],
"requestId": "AUD-MHH3K2QZ-4F1C9B2E7D10",
"token": "bst_q3Jm0b8vN2kXo7rT5wYz1cE4hL6pA9sD0fG2jK8uV3i"
}{
"decision": "ALLOWED",
"riskScore": 30,
"reasons": [
"Test mode: nobonus@test.inlineshield.io always gives ALLOWED / allow_no_bonus."
],
"requestId": "AUD-MHH3K30A-91B7C2D40E55",
"token": "bst_q3Jm0b8vN2kXo7rT5wYz1cE4hL6pA9sD0fG2jK8uV3i"
}{
"decision": "QUARANTINED",
"riskScore": 50,
"reasons": [
"Test mode: review@test.inlineshield.io always gives QUARANTINED / review."
],
"requestId": "AUD-MHH3K31C-0C3D5E7F9A21",
"token": "bst_q3Jm0b8vN2kXo7rT5wYz1cE4hL6pA9sD0fG2jK8uV3i"
}{
"decision": "QUARANTINED",
"riskScore": 60,
"reasons": [
"Test mode: kyc@test.inlineshield.io always gives QUARANTINED / require_kyc."
],
"requestId": "AUD-MHH3K32F-7E2A4C6B8D90",
"token": "bst_q3Jm0b8vN2kXo7rT5wYz1cE4hL6pA9sD0fG2jK8uV3i"
}{
"decision": "BLOCKED",
"riskScore": 95,
"reasons": [
"Test mode: block@test.inlineshield.io always gives BLOCKED / block."
],
"requestId": "AUD-MHH3K33H-5B9D1F3A7C02",
"token": "bst_q3Jm0b8vN2kXo7rT5wYz1cE4hL6pA9sD0fG2jK8uV3i"
}400 Bad Request
No email, or a body that is not valid JSON.
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Values: BAD_REQUEST |
message | string | yes |
401 Unauthorized
No site key, or an unknown or revoked one.
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Values: INVALID_SITE_KEY |
message | string | yes |
{
"error": "INVALID_SITE_KEY",
"message": "Unknown or revoked site key."
}402 Payment Required
The monthly quota is used up (no token is issued).
| Field | Type | Required | Description |
|---|---|---|---|
error | "QUOTA_EXCEEDED" | yes | |
message | string | yes | |
used | integer | yes | at least 0 |
limit | integer | yes | at least 0 |
resetsAt | string (date-time) | yes | The start of the next UTC month. |
403 Forbidden
The Origin is missing or not one of the site key's domains.
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Values: ORIGIN_NOT_ALLOWED |
message | string | yes |
{
"error": "ORIGIN_NOT_ALLOWED",
"message": "Requests from evil.test are not allowed for this site key. Add evil.test to the site's domains."
}429 Too Many Requests
A per-minute limit, or (test site key) the daily test limit.
Headers: Retry-After.
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Values: RATE_LIMIT_EXCEEDED, TEST_LIMIT |
message | string | yes |
500 Internal Server Error
The check failed. inlineshield.js then lets the form submit (fail open).
| Field | Type | Required | Description |
|---|---|---|---|
error | string | yes | Values: VERIFICATION_ERROR, PERSISTENCE_FAILURE |
message | string | yes |
Errors
Errors are JSON, { "error": "CODE", "message": "…" }. Branch on error, never on message.
| HTTP status | error | When |
|---|---|---|
400 | BAD_REQUEST | No email, or a body that is not valid JSON. |
401 | INVALID_SITE_KEY | No site key, or an unknown or revoked one. |
402 | QUOTA_EXCEEDED | The monthly quota is used up (no token is issued). |
403 | ORIGIN_NOT_ALLOWED | The Origin is missing or not one of the site key's domains. |
429 | RATE_LIMIT_EXCEEDED, TEST_LIMIT | A per-minute limit, or (test site key) the daily test limit. |
500 | VERIFICATION_ERROR, PERSISTENCE_FAILURE | The check failed. inlineshield.js then lets the form submit (fail open). |
See all errors for every endpoint.
Headers and limits
| Response header | Meaning |
|---|---|
X-Quota-Limit | Live requests only: the monthly quota of the plan. |
X-Quota-Used | Live requests only: screenings counted this month, this one included. |
X-Quota-Reset | Live requests only: when the count resets (the start of the next UTC month). |
X-Quota-State | Live requests only: ok, warn (80% used), limit (100%), grace (over 100%, still served) or blocked. |
Retry-After | Seconds to wait before trying again. |
The limits per endpoint and plan are on Rate limits and quotas.