Skip to main content
A ShieldLabs identification is asynchronous by design. The browser snippet collects signals and posts them. The server computes the Risk Score (0–100) a moment later and delivers it two ways: a webhook push and a History API read. There is no synchronous score endpoint: the POST that ingests signals returns an acknowledgment, not the score. The RequestID is the join key that ties the three steps together.
You normally do not call rest.shieldlabs.ai yourself. The JS snippet posts to it automatically. Your server-side work is to receive the webhook and, when you need a guaranteed read, query the History API.

The three steps

1

Snippet posts signals (browser → rest.shieldlabs.ai)

Load the current snippet from the official CDN. It generates a per-call RequestID (a client UUID) and posts collected signals to rest.shieldlabs.ai. The response is an acknowledgment (the client IP as a JSON string), not the score.
2

Server scores asynchronously (~1s)

The server computes the Risk Score. The webhook carries the breakdown in data.signals as { name, weight } (stable slugs, e.g. antidetect_browser). History/Management snapshots use a Details array (Value, Description) — that is not the webhook body.
3

Score is delivered (webhook + History API)

The server pushes one final webhook per check (typically about a second; up to 60 seconds in some cases). You can also read the result any time from the History API by request_id.

Step 1: The snapshot POST (acknowledgment, not a score)

The JS snippet calls ingest for you. Load it from https://cdn.shieldlabs.ai. Do not self-host, mirror, bundle, or pin copies of the agent.
  • {requestID} is a client-generated UUID, unique per identify call. It is the join key across snapshot → webhook → history.
  • publicKey is your per-domain Public Key. It is safe to expose in the browser.
The ingest response is an acknowledgment, not the result:
The body is the client IP as a JSON string (HTTP 200). It confirms the signals were received and billed. It does not contain the VisitorID, DeviceID, or Risk Score. Those are computed server-side and delivered in Step 3.
This response is a receipt, not the score; read the result from the webhook or History API.
In the browser, the snippet surfaces the requestID through optional onInitialized, so you can correlate client and server records. The snapshot HTTP body remains a receipt; it is not copied onto that object.
The snippet install guide has the full method list and framework examples.

Step 2: Why scoring is asynchronous

The Risk Score is not available the instant ingest acknowledges the visit. Scoring runs on the server and typically takes about a second (up to 60 seconds in some cases). ShieldLabs then delivers one final webhook per check.

Step 3: Receiving the score

You get the score two ways. Use both: the webhook for low latency, the History API as the guaranteed-read fallback.

Webhook (push)

The server POSTs the score to each enabled webhook endpoint once per check, joined by request_id: The webhook body is a signed snake_case envelope. The signature travels in the X-Shield-Signature header:
Full field schema: WebhookEvent.
Verify X-Shield-Signature on the raw request body, keyed with that endpoint’s whsec_… signing secret (not the domain Secret Key). See the verification recipe.
Webhook delivery is at-most-once with no retries (a ~1 second timeout, no backoff, no dead-letter queue). Make your handler idempotent on request_id and use the History API for anything that must not be missed. Full payload, signature verification in Node/Go/Python, and delivery guarantees are in Webhooks.

History API (read)

You can read the scored result for any RequestID from the History API. This is the authoritative, pull-based path and the right choice when you cannot risk a dropped webhook.
The response is a { data, total } envelope with snapshots (newest first). You can search by request_id, visitor_id, device_id, user_hid, ip, session_id, or cookie_id. History reads through account.shieldlabs.ai do not consume request balance. The full schema is in the Server API reference.

The RequestID lifecycle

RequestID is the single value that lets you stitch the asynchronous pieces together:

Snapshot

Minted client-side as a UUID when the snippet runs. Returned to your page in the snippet callback.

Webhook

Echoed back as request_id. One scored POST per check.

History

Queryable as the request_id search type to read the stored snapshot any time.
Persist the requestID early, record score and signals idempotently when the webhook arrives, and fall back to GET /api/v1/history/request_id/{requestID} on account.shieldlabs.ai if it never does (allow up to ~60s). The full reliability pattern, with signature verification, is in Webhooks.

What you do with the score

ShieldLabs scores. Your code decides. The Risk Score lands in the 0–100 range and falls into four Risk Score bands you can act on, with your application the actor for allow, challenge, review, or block. Decide on score + webhook signals (or History Details) + action context, never the number alone: a legitimate user can score high (corporate proxy, VPN, privacy browser). The per-band playbook gives threshold guidance and worked examples.

Next steps

Webhooks

Full payload, X-Shield-Signature verification, and at-most-once delivery.

Server API

History search, the snapshot schema, profile, and billing.

Risk Score

How the 0–100 explainable score and the Clean / Low / Medium / High bands work.

Identifiers

RequestID, SessionID, CookieID, DeviceID, VisitorID, and UserHID mechanics.