openapi: "3.1.0"
info:
title: ShieldLabs API
version: "1.2"
description: |
The ShieldLabs API surfaces visitor identification, anonymity detection, and
abuse and fraud prevention for your application.
There are three surfaces:
- The **JS snippet** posts collected signals to `rest.shieldlabs.ai` automatically.
You do not call this endpoint yourself. See [Install the snippet](/setup/snippet).
- **Webhooks** deliver the Risk Score and signals to your server shortly after a
visit. See [Webhooks](/api/webhooks). Contract: schema_version `2026-06-01`
(`identification.scored` / `webhook.ping`), signature in `X-Shield-Signature`.
- The **Server API** has two backend hosts:
- **History API** (recommended) on `account.shieldlabs.ai/api` — implemented by
**Shield.Portal.Admin**. Private API Key (`Authorization: Bearer sec_…`).
Envelope `{ data, total }`. See [Server API](/api/server-api).
- **Management API** on `api.shieldlabs.ai` — implemented by **Shield.Core**.
Secret Key in headers (`X-Shield-Domain` + `Authorization: Bearer`).
Profile, balance, and alternate billed History path (PascalCase array).
The Risk Score is an integer from 0 to 100. ShieldLabs scores visits; your own
code decides whether to allow, challenge, review, or block.
contact:
email: support@shieldlabs.ai
servers:
- url: https://account.shieldlabs.ai/api
description: History API (Shield.Portal.Admin — Private API Key, recommended for snapshot reads)
- url: https://api.shieldlabs.ai
description: Management API (Shield.Core — Secret Key — profile, balance, billed History)
tags:
- name: history-api
description: |
Recommended snapshot reads on account.shieldlabs.ai (service: Shield.Portal.Admin).
Authenticated with Private API Key. Response envelope `{ data, total }`.
- name: management-api
description: |
Profile, balance, and alternate History on api.shieldlabs.ai (service: Shield.Core).
Authenticated with Secret Key headers. History returns a PascalCase JSON array.
paths:
/api/v1/history/{search_type}/{value}:
get:
tags: [history-api]
summary: Search history by identifier (recommended)
description: |
Returns stored snapshots that match an identifier, newest first, in a paginated
`{ data, total }` envelope. Does not consume request balance.
Served by Shield.Portal.Admin on account.shieldlabs.ai.
operationId: searchHistoryAccount
security:
- PrivateApiKey: []
parameters:
- name: search_type
in: path
required: true
schema:
type: string
enum: [ip, user_hid, visitor_id, request_id, device_id, session_id, cookie_id]
- name: value
in: path
required: true
schema:
type: string
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
responses:
"200":
description: Paginated snapshot list
content:
application/json:
schema:
$ref: "#/components/schemas/HistoryEnvelope"
"401":
$ref: "#/components/responses/UnauthorizedJson"
/v1/profile:
get:
tags: [management-api]
summary: Get domain profile
description: |
Returns the profile for a domain: remaining request balance and masked keys. Free (0 requests).
operationId: getProfileV1
security:
- SecretKeyHeader: []
responses:
"200":
description: Domain profile
content:
application/json:
schema:
$ref: "#/components/schemas/Profile"
"401":
$ref: "#/components/responses/UnauthorizedEmpty"
/v1/history/{type}/{value}:
get:
tags: [management-api]
summary: Search history by identifier (Management API)
description: |
Returns stored snapshots in a PascalCase JSON array, newest first. Each returned
row consumes one request from your balance (an empty result still consumes one).
Prefer `/api/v1/history/…` on account.shieldlabs.ai for new integrations.
Served by Shield.Core on api.shieldlabs.ai.
operationId: searchHistoryV1
security:
- SecretKeyHeader: []
parameters:
- name: type
in: path
required: true
schema:
type: string
enum: [ip, user_hid, visitor_id, request_id, device_id, session_id, cookie_id]
- name: value
in: path
required: true
schema:
type: string
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 100
responses:
"200":
description: Matching snapshots, newest first
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Snapshot"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/UnauthorizedEmpty"
"402":
$ref: "#/components/responses/OutOfRequests"
"404":
description: Unsupported identifier type
"429":
$ref: "#/components/responses/TooManyRequests"
"503":
$ref: "#/components/responses/ServiceUnavailable"
webhooks:
identificationScored:
post:
summary: identification.scored delivered
description: |
ShieldLabs POSTs a signed JSON envelope to your configured webhook endpoint after
a visit is scored. One delivery per check (at-most-once, no retries).
Verify `X-Shield-Signature: sha256=<hex>` (HMAC-SHA256 over the raw body with your
endpoint secret). Treat `data.request_id` as an idempotency key.
Schema version is `2026-06-01` (`schema_version` field). Ping/test deliveries use
`event_type: webhook.ping` with no `data`.
parameters:
- name: X-Shield-Signature
in: header
required: true
description: |
HMAC-SHA256 of the raw request body, hex-encoded, prefixed with `sha256=`.
Example: `sha256=9f1c2b3a…`.
schema:
type: string
- name: Content-Type
in: header
required: true
schema:
type: string
enum: [application/json]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEvent"
examples:
scored:
summary: identification.scored
value:
event_type: identification.scored
schema_version: "2026-06-01"
created_at: "2026-06-26T14:20:42Z"
data:
request_id: "13f84f05-7c2a-4e9b-9f1d-2a6b8c0e4d11"
visitor_id: "161dfbad-8e7f-4a6b-9c5d-0e1f2a3b4c5d"
device_id: "5eb7fd5c-2a1b-4c3d-9e8f-7a6b5c4d3e2f"
session_id: "7a1b2c3d-4e5f-6789-abcd-ef0123456789"
cookie_id: "3f2e1d0c-9b8a-7654-3210-fedcba987654"
user_hid: null
domain: example.com
public_ip: { ip: "203.0.113.42", country: "US" }
local_ip: { ip: "198.51.100.23", country: "DE" }
connection_type: proxy
os: Windows
browser: Chrome
device_type: desktop
traffic_source:
channel: "Google Ads"
referrer_domain: google.com
landing_url: "https://example.com/lp?gclid=abc123"
click_id_type: gclid
utm_source: google
utm_medium: cpc
utm_campaign: summer_sale
utm_content: ad_a
utm_term: "buy shoes"
risk_score: 30
signals:
- { name: proxy, weight: 10 }
- { name: datacenter_ip, weight: 10 }
- { name: abuser, weight: 10 }
detection_flags:
vpn: false
privacy_relay: false
browser_vpn_proxy: false
tor: false
proxy: true
datacenter_ip: true
abuser: true
os_mismatch: false
os_not_detected: false
timezone_mismatch: false
anti_detect_browser: false
browser_automation: false
ip_mismatch: true
incognito: false
search_bot: false
suspicious_paid_click: false
javascript_disabled: false
stun_request_seen: true
stun_not_checked: false
observed_at: "2026-06-26T14:20:42Z"
ping:
summary: webhook.ping
value:
event_type: webhook.ping
schema_version: "2026-06-01"
created_at: "2026-06-26T14:20:42Z"
responses:
"200":
description: Acknowledged. Return 200 quickly.
components:
securitySchemes:
PrivateApiKey:
type: http
scheme: bearer
description: Private API Key from the dashboard API tab (`sec_…`).
SecretKeyHeader:
type: http
scheme: bearer
description: Secret Key plus `X-Shield-Domain` header with your registered domain.
responses:
UnauthorizedEmpty:
description: Missing or invalid credentials, or the domain is disabled (empty body)
UnauthorizedJson:
description: Missing or invalid Private API Key
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
BadRequest:
description: Invalid parameters (bare JSON string on Management API)
OutOfRequests:
description: The domain has no remaining request balance (empty body)
TooManyRequests:
description: Per-IP rate limit exceeded
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
ServiceUnavailable:
description: Gateway concurrency cap reached
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
schemas:
Error:
type: object
properties:
error:
type: string
examples: ["too many requests", "invalid api key"]
HistoryEnvelope:
type: object
properties:
data:
type: array
items:
$ref: "#/components/schemas/AccountSnapshot"
total:
type: integer
AccountSnapshot:
type: object
description: Snapshot row from the History API (snake_case).
properties:
request_id:
type: string
format: uuid
session_id:
type: string
format: uuid
cookie_id:
type: string
format: uuid
device_id:
type: string
format: uuid
visitor_id:
type: string
format: uuid
ip:
type: string
score:
type: integer
score_details:
type: string
description: JSON string of signal objects with Value and Description fields.
created_at:
type: string
ScoreDetail:
type: object
description: Signal row on Management API Snapshot.Details (PascalCase). Not used in webhooks.
properties:
Value:
type: integer
Description:
type: string
WebhookIPAddress:
type: object
properties:
ip:
type: string
country:
type: string
WebhookSignal:
type: object
required: [name, weight]
properties:
name:
type: string
description: Stable signal slug (e.g. proxy, vpn, datacenter_ip).
weight:
type: integer
description: Points contributed to risk_score. Debug entries with weight 0 are omitted.
WebhookTrafficSource:
type: object
properties:
channel:
type: string
referrer_domain:
type: string
landing_url:
type: string
click_id_type:
type: string
utm_source:
type: string
utm_medium:
type: string
utm_campaign:
type: string
utm_content:
type: string
utm_term:
type: string
WebhookDetectionFlags:
type: object
description: Denormalized detection booleans from Shield.Core entity.WebhookDetectionFlags.
properties:
vpn:
type: boolean
privacy_relay:
type: boolean
browser_vpn_proxy:
type: boolean
tor:
type: boolean
proxy:
type: boolean
datacenter_ip:
type: boolean
abuser:
type: boolean
os_mismatch:
type: boolean
os_not_detected:
type: boolean
timezone_mismatch:
type: boolean
anti_detect_browser:
type: boolean
browser_automation:
type: boolean
ip_mismatch:
type: boolean
incognito:
type: boolean
search_bot:
type: boolean
suspicious_paid_click:
type: boolean
javascript_disabled:
type: boolean
stun_request_seen:
type: boolean
stun_not_checked:
type: boolean
WebhookScoredData:
type: object
description: Payload inside identification.scored (Shield.Core entity.WebhookScoredData).
required:
- request_id
- visitor_id
- device_id
- risk_score
- signals
- detection_flags
- observed_at
properties:
request_id:
type: string
format: uuid
description: Unique per visit. Use as an idempotency key.
visitor_id:
type: string
format: uuid
device_id:
type: string
format: uuid
session_id:
type: string
format: uuid
cookie_id:
type: string
format: uuid
user_hid:
type: ["string", "null"]
description: Your hashed user identifier, if provided; null when empty.
domain:
type: string
public_ip:
$ref: "#/components/schemas/WebhookIPAddress"
local_ip:
$ref: "#/components/schemas/WebhookIPAddress"
connection_type:
type: string
description: Observed connection class; new values may appear.
enum: [direct, mobile, vpn, proxy, tor, privacy_relay, browser_vpn_proxy, unknown]
os:
type: string
browser:
type: string
device_type:
type: string
traffic_source:
$ref: "#/components/schemas/WebhookTrafficSource"
risk_score:
type: integer
minimum: 0
maximum: 100
signals:
type: array
items:
$ref: "#/components/schemas/WebhookSignal"
detection_flags:
$ref: "#/components/schemas/WebhookDetectionFlags"
observed_at:
type: string
format: date-time
WebhookEvent:
type: object
description: |
POST body envelope (Shield.Core entity.WebhookEvent). Signature is NOT in the body —
verify the X-Shield-Signature header.
required: [event_type, schema_version, created_at]
properties:
event_type:
type: string
enum: [identification.scored, webhook.ping]
schema_version:
type: string
const: "2026-06-01"
created_at:
type: string
format: date-time
data:
$ref: "#/components/schemas/WebhookScoredData"
description: Present for identification.scored; omitted for webhook.ping.
Snapshot:
type: object
description: Snapshot from the Management API History path (PascalCase array element).
properties:
RequestID:
type: string
format: uuid
SessionID:
type: string
format: uuid
CookieID:
type: string
format: uuid
DeviceID:
type: string
format: uuid
VisitorID:
type: string
format: uuid
IP:
type: string
ConnectionType:
type: string
enum: [direct, mobile, vpn, proxy, tor, privacy_relay, unknown]
WebRtcHIP:
type: string
OS:
type: string
Browser:
type: string
DeviceType:
type: string
Country:
type: string
UserHID:
type: string
Score:
type: integer
Details:
type: array
items:
$ref: "#/components/schemas/ScoreDetail"
LastRequestTime:
type: string
format: date-time
Profile:
type: object
properties:
Domain:
type: string
Weight:
type: integer
PublicKey:
type: string
Secret:
type: string
CreatedAt:
type: string
format: date-time