stile
HTTP API

Compliance

Check product-level regulatory compliance rules by jurisdiction. Resolve required age tiers, prohibited products, and allowed verification methods.

The Compliance API lets you check regulatory rules for specific products in specific jurisdictions. Use it to:

  • Determine the required age tier before creating a verification session
  • Check if a product is prohibited in the user's jurisdiction
  • Get the allowed verification methods for a jurisdiction
  • Handle mixed carts with multiple product types by resolving the most restrictive rules

Widget handles this automatically

If you're using the widget SDK with the products attribute, compliance is resolved automatically — you don't need to call this API directly. This API is for server-side integrations where you create sessions manually.

Check compliance

GET/v1/compliance/check

Returns compliance details for one or more product types in a jurisdiction, plus a most_restrictive summary that merges the rules across all products.

ParameterTypeDescription
use_casesrequiredstringComma-separated product types to check (max 10). E.g. "alcohol_delivery,tobacco_nicotine".
jurisdictionstringJurisdiction to check against (e.g. "US-CA", "US-UT"). If omitted, auto-detected from request IP / geo headers.

Available product types

Product typeDescription
alcohol_deliveryAlcohol delivery (online orders)
alcohol_in_personAlcohol retail (point-of-sale)
cannabis_dispensaryCannabis dispensary sales
cannabis_deliveryCannabis delivery
gambling_onlineOnline gambling / iGaming
gambling_in_personIn-person gambling / casinos
tobacco_nicotineTobacco and nicotine products
adult_contentAdult content / pornography
social_mediaSocial media age gates
firearmsFirearms sales

Example: single product

curl "https://api.stile.dev/v1/compliance/check?use_cases=alcohol_delivery&jurisdiction=US-CA" \
  -H "Authorization: Bearer vk_test_..."
import requests

res = requests.get(
    "https://api.stile.dev/v1/compliance/check",
    headers={"Authorization": "Bearer vk_test_..."},
    params={"use_cases": "alcohol_delivery", "jurisdiction": "US-CA"},
)
result = res.json()
print(result["most_restrictive"]["required_age_tier"])
# "over_21"
req, _ := http.NewRequest("GET", "https://api.stile.dev/v1/compliance/check?use_cases=alcohol_delivery&jurisdiction=US-CA", nil)
req.Header.Set("Authorization", "Bearer vk_test_...")
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);
// "over_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.

curl "https://api.stile.dev/v1/compliance/check?use_cases=alcohol_delivery,tobacco_nicotine&jurisdiction=US-UT" \
  -H "Authorization: Bearer vk_test_..."
import requests

res = requests.get(
    "https://api.stile.dev/v1/compliance/check",
    headers={"Authorization": "Bearer vk_test_..."},
    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.dev/v1/compliance/check?use_cases=alcohol_delivery,tobacco_nicotine&jurisdiction=US-UT", nil)
req.Header.Set("Authorization", "Bearer vk_test_...")
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 — pass the use case, age tier is resolved internally
const session = await stile.verificationSessions.create({
  type: "age",
  use_case: "alcohol_delivery",
});

Response

The response includes 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": "over_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": "over_21",
    "allowed_methods": ["mdl", "facial_age", "carrier_lookup", "document_capture"],
    "consent_required": false,
    "data_retention_days": null,
    "any_prohibited": false,
    "prohibited_use_cases": []
  }
}

Per-result fields

ParameterTypeDescription
use_casestringThe product type that was checked.
prohibitedbooleanWhether this product is prohibited in the jurisdiction.
prohibited_reasonstring | nullWhy the product is prohibited (e.g. state law citation).
required_age_tierstring | nullThe age tier required for this product. E.g. "over_21", "over_18".
allowed_methodsstring[]Verification methods permitted in this jurisdiction for this product.
governing_regulatorstring | nullThe regulatory body (e.g. State ABC, FDA).
consent_requiredbooleanWhether explicit user consent is required.
penalty_descriptionstring | nullPenalty for non-compliance (e.g. "Up to $10K/day per violation").
penalty_typestring | null"CIVIL", "CRIMINAL", or "BOTH".
credential_validity_daysnumberHow 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).

Most-restrictive summary fields

ParameterTypeDescription
required_age_tierstring | nullThe highest age tier across all non-prohibited products.
allowed_methodsstring[]Intersection of allowed methods across all non-prohibited products.
any_prohibitedbooleanTrue if any product type is prohibited in this jurisdiction.
prohibited_use_casesstring[]List of product types that are prohibited.
consent_requiredbooleanTrue if any product requires explicit consent.
data_retention_daysnumber | nullShortest data retention limit across all products.

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:

  1. Call stile.compliance.check() with the product types in the user's cart and their jurisdiction
  2. If any products are prohibited, remove them from the cart or show an error
  3. Pass the use_case to stile.verificationSessions.create() — the age tier is resolved internally

Widget flow (automatic):

  1. Set products="alcohol_delivery,tobacco_nicotine" on the <stile-button> element
  2. The widget calls the Compliance API internally, resolves the age tier, and creates the session — no server code needed

On this page