Compliance
Look up regulatory rules by product type and jurisdiction — required age tiers, prohibited products, allowed verification methods, and most-restrictive resolution for mixed carts.
The Compliance API tells you what the rules are before you ask anyone to verify. Give it one or more product types and a jurisdiction, and it returns the required age tier, whether the product is prohibited, and which verification methods that jurisdiction permits. Use it to:
- Pre-check carts — catch prohibited products before checkout, not after the user has verified
- Show age requirements in your UI — display "21+ required in California" before launching verification
- Pick between workflows — when you run multiple published workflows, choose the one whose use case matches what the rules require
- Resolve mixed carts — multiple product types collapse into one
most_restrictivesummary: the highest age tier, the intersected allowed methods
Sessions handle this automatically
When you create a verification session, compliance is resolved internally from the workflow's use case and the user's jurisdiction — you don't need to call this API first. Use it when you want to inspect the rules yourself: pre-checking carts for prohibited products, showing age requirements in your UI, or picking between workflows.
Check compliance
/v1/compliance/checkReturns compliance details for one or more product types in a jurisdiction, plus a most_restrictive summary that merges the rules across all products.
| Parameter | Type | Description |
|---|---|---|
use_casesrequired | string | Comma-separated product types to check (max 10). E.g. "alcohol_delivery,tobacco_nicotine". |
jurisdiction | string | Jurisdiction to check against (e.g. "US-CA", "US-UT"). If omitted, auto-detected from request IP / geo headers. |
Available product types
| Product type | Description |
|---|---|
alcohol_delivery | Alcohol delivery (online orders) |
alcohol_in_person | Alcohol retail (point-of-sale) |
cannabis_dispensary | Cannabis dispensary sales |
cannabis_delivery | Cannabis delivery |
gambling_online | Online gambling / iGaming |
gambling_in_person | In-person gambling / casinos |
tobacco_nicotine | Tobacco and nicotine products |
adult_content | Adult content / pornography |
social_media | Social media age gates |
firearms | Firearms sales |
Example: single product
Check one product type against an explicit jurisdiction:
curl "https://api.stile.id/v1/compliance/check?use_cases=alcohol_delivery&jurisdiction=US-CA" \
-H "Authorization: Bearer stile_sk_..."import requests
res = requests.get(
"https://api.stile.id/v1/compliance/check",
headers={"Authorization": "Bearer stile_sk_..."},
params={"use_cases": "alcohol_delivery", "jurisdiction": "US-CA"},
)
result = res.json()
print(result["most_restrictive"]["required_age_tier"])
# "min_age_21"req, _ := http.NewRequest("GET", "https://api.stile.id/v1/compliance/check?use_cases=alcohol_delivery&jurisdiction=US-CA", nil)
req.Header.Set("Authorization", "Bearer stile_sk_...")
res, _ := http.DefaultClient.Do(req)const result = await stile.compliance.check({
use_cases: ["alcohol_delivery"],
jurisdiction: "US-CA",
});
console.log(result.most_restrictive.required_age_tier);
// "min_age_21"Example: mixed cart
When a cart contains multiple product types, the API returns per-product details and a merged most_restrictive summary with the highest age tier and intersected allowed methods. If any single product is prohibited, any_prohibited flips to true and the offending product types are listed in prohibited_use_cases.
curl "https://api.stile.id/v1/compliance/check?use_cases=alcohol_delivery,tobacco_nicotine&jurisdiction=US-UT" \
-H "Authorization: Bearer stile_sk_..."import requests
res = requests.get(
"https://api.stile.id/v1/compliance/check",
headers={"Authorization": "Bearer stile_sk_..."},
params={
"use_cases": "alcohol_delivery,tobacco_nicotine",
"jurisdiction": "US-UT",
},
)
result = res.json()
if result["most_restrictive"]["any_prohibited"]:
print("Cannot sell:", result["most_restrictive"]["prohibited_use_cases"])req, _ := http.NewRequest("GET", "https://api.stile.id/v1/compliance/check?use_cases=alcohol_delivery,tobacco_nicotine&jurisdiction=US-UT", nil)
req.Header.Set("Authorization", "Bearer stile_sk_...")
res, _ := http.DefaultClient.Do(req)const result = await stile.compliance.check({
use_cases: ["alcohol_delivery", "tobacco_nicotine"],
jurisdiction: "US-UT",
});
// Check if any products are prohibited in this jurisdiction
if (result.most_restrictive.any_prohibited) {
console.log("Cannot sell:", result.most_restrictive.prohibited_use_cases);
// Remove prohibited items from the cart
}
// Per-product details
for (const r of result.results) {
console.log(r.use_case, r.prohibited ? "PROHIBITED" : r.required_age_tier);
}
// Create a session — the workflow carries the use case, and the
// age tier is resolved internally
const session = await stile.verificationSessions.create({
type: "age",
workflow_id: "wf_YOUR_WORKFLOW_ID",
});Response
The response includes the resolved jurisdiction, a results array (one entry per product type), and a most_restrictive summary.
{
"jurisdiction": "US-CA",
"jurisdiction_source": "explicit",
"results": [
{
"use_case": "alcohol_delivery",
"jurisdiction": "US-CA",
"prohibited": false,
"prohibited_reason": null,
"required_age_tier": "min_age_21",
"allowed_methods": ["mdl", "facial_age", "carrier_lookup", "document_capture"],
"mdl_acceptance_status": "supplementary",
"governing_regulator": "State Alcohol Beverage Control Boards",
"consent_required": false,
"credential_validity_days": 365,
"rule_version": "2026-04-02"
}
],
"most_restrictive": {
"required_age_tier": "min_age_21",
"allowed_methods": ["mdl", "facial_age", "carrier_lookup", "document_capture"],
"consent_required": false,
"data_retention_days": null,
"any_prohibited": false,
"prohibited_use_cases": []
}
}Top-level fields
| Parameter | Type | Description |
|---|---|---|
jurisdiction | string | The jurisdiction the check was resolved against. |
jurisdiction_source | string | "explicit" when you passed a jurisdiction, "auto_detected" when it was resolved from the request. |
results | object[] | One entry per product type checked. See per-result fields below. |
most_restrictive | object | Merged summary across all product types. See summary fields below. |
Per-result fields
| Parameter | Type | Description |
|---|---|---|
use_case | string | The product type that was checked. |
jurisdiction | string | The jurisdiction this rule was evaluated against. |
prohibited | boolean | Whether this product is prohibited in the jurisdiction. |
prohibited_reason | string | null | Why the product is prohibited (e.g. state law citation). |
required_age_tier | string | null | The age tier required for this product. E.g. "min_age_21", "min_age_18". |
allowed_methods | string[] | Verification methods permitted in this jurisdiction for this product. |
mdl_acceptance_status | string | How mobile driver's licenses are treated under this rule: "full", "supplementary", "not_recognized", or "unknown". |
governing_regulator | string | null | The regulatory body (e.g. State ABC, FDA). |
consent_required | boolean | Whether explicit user consent is required. |
penalty_description | string | null | Penalty for non-compliance (e.g. "Up to $10K/day per violation"). |
penalty_type | string | null | "CIVIL", "CRIMINAL", or "BOTH". |
credential_validity_days | number | How long a verification credential remains valid (in days). When a user verifies, their credential expires after this many days — forcing re-verification. Varies by jurisdiction and product type (e.g., 30 days for gambling, 365 for alcohol). |
rule_version | string | null | Version identifier of the rule set this result was computed from. |
Most-restrictive summary fields
| Parameter | Type | Description |
|---|---|---|
required_age_tier | string | null | The highest age tier across all non-prohibited products. |
allowed_methods | string[] | Intersection of allowed methods across all non-prohibited products. |
any_prohibited | boolean | True if any product type is prohibited in this jurisdiction. |
prohibited_use_cases | string[] | List of product types that are prohibited. |
consent_required | boolean | True if any product requires explicit consent. |
data_retention_days | number | null | Shortest data retention limit across all products. |
Age tiers are ordered: min_age_13 < min_age_16 < min_age_18 < min_age_21. The summary returns the highest tier required by any non-prohibited product in the cart — a verification at a higher tier satisfies any lower requirement, never the reverse.
Typical integration flow
The Compliance API is informational — it tells you the rules but doesn't enforce them. Your application decides what to do with the results.
Server-side flow:
- Call
stile.compliance.check()with the product types in the user's cart and their jurisdiction - If any products are prohibited, remove them from the cart or show an error before starting verification
- Create the session with the matching
workflow_id— the age tier is resolved internally from the workflow's use case
Widget flow (automatic):
- Point the widget at a published workflow (
workflow-id, or let yoursession-urlendpoint choose one) - Session creation resolves compliance from the workflow internally — no extra API call needed
Next steps
Verification Sessions
Create and manage sessions — the workflow resolves compliance for you at session time.
Integration Guide
Backend session creation, prohibited products, and returning users end to end.
Node.js SDK
Typed stile.compliance.check() plus the rest of the server-side client.
API Overview
Authentication, error shapes, rate limits, and idempotency across the API.