Docs

Build with AI

Feed the Stile docs to your AI assistant or coding agent — llms.txt, a full-corpus file, and the OpenAPI spec.

Most Stile integrations are now written with an AI assistant in the loop. These docs are published in machine-friendly formats so your assistant works from the real API surface instead of guessing:

ResourceURLWhat it is
llms.txtdocs.stile.id/llms.txtIndex of every docs page with descriptions, per the llms.txt convention.
llms-full.txtdocs.stile.id/llms-full.txtThe complete public integration documentation as one markdown file.
OpenAPI 3.1 specdocs.stile.id/openapi.yamlMachine-readable schema for the documented public integration surface.

Using them

Coding agents (Claude Code, Codex, Cursor, Copilot Workspace) — point the agent at https://docs.stile.id/llms-full.txt in your prompt, or download it into the repo so it's part of the agent's searchable context:

curl -o docs/stile-docs.md https://docs.stile.id/llms-full.txt

Chat assistants (Claude, ChatGPT) — paste the contents of llms-full.txt into the conversation (or attach it as a file), then describe your stack and what you're building.

Cursor — add https://docs.stile.id/llms-full.txt as a custom docs source (Settings → Indexing & Docs), then reference it with @Docs in chat.

SDK generators and API tooling — consume https://docs.stile.id/openapi.yaml directly.

Starter prompt

A prompt that reliably produces a working integration. Replace the bracketed backend with your own:

You are integrating Stile age verification (docs: https://docs.stile.id/llms-full.txt).

My backend: [LANGUAGE, FRAMEWORK, AND DEPLOYMENT PLATFORM].
My workflow ID: wf_YOUR_WORKFLOW_ID (already published in the Stile dashboard).

Build:
1. An authenticated, per-user-rate-limited POST /api/start-verification route.
   Read orderId from the session-url path/query and load an order owned by the
   authenticated user. Using the backend's standard HTTP client, POST
   https://api.stile.id/v1/verification_sessions with Authorization: Bearer
   STILE_API_KEY, Content-Type: application/json, a stable Idempotency-Key,
   and { type: "age", workflow_id, email: authenticatedUser.email,
   client_reference_id: order.id }. Store session.id on the order, and return
   { session_id, client_secret, methods, age_tier }. Do not install or import
   @stile/node.
2. A checkout page that loads https://js.stile.id/v1/stile.js and renders
   <stile-frame mode="modal"
   session-url="/api/start-verification?orderId=order_123"
   workflow-id="wf_YOUR_WORKFLOW_ID">.
3. A POST /api/webhooks route that reads the raw body before JSON parsing.
   Parse t and v1 from Stile-Signature, reject timestamps more than 300
   seconds from the current time, compute lowercase hex
   HMAC-SHA256(STILE_WEBHOOK_SECRET,
   "{t}.{raw_body}"), and compare in constant time. Reject failures with 400.
   Read the session from event.data.object. In one database transaction, claim
   unique event.id and update only the pending order whose stored session ID
   and id/client_reference_id both match. Return 500 if persistence fails so
   Stile retries.

Rules: use direct HTTPS from any backend. @stile/node is not a public npm
package, so do not generate an install command or SDK import unless package
access is explicitly confirmed. Never trust the client-side stile:verified
event for fulfillment; use only the signed webhook. Use stile_sk_ keys and
test against the 500-session monthly sandbox quota. Never expose the secret
key to the browser.

Canonical integration facts

  • Webhook resources are always nested at event.data.object; never use event.data.id.
  • The supported public backend path is direct HTTPS to https://api.stile.id/v1/ from any language. @stile/node is an unpublished private preview, so agents must not generate package installation or imports without confirmed access.
  • Deduplicate business work on event.id. Automatic and manual retries reuse the delivery record ID; another subscribed endpoint has its own delivery record for the same event.
  • Stile automatically retries webhook delivery for 429, 5xx, network failures, and timeouts. Other 4xx responses are permanent failures.
  • The browser's stile:verified event, redirect query parameters, and injected form fields are not proof. Fulfill only from state written by a verified webhook.
  • Primary widget paths are mdl and document_capture, plus nfc_passport in supported native embeds. selfie_liveness and selfie_match are active layers; self_attestation is an additional declaration and must not be the only hosted-widget path. ip_analysis, device_risk, and geolocation are passive supplementary checks. Publishing rejects mid, eudi_pid, facial_age, carrier_lookup, open_banking, parental_consent, student, and standalone apple_wallet steps as not implemented.
  • Session expiry defaults to 24 hours but can be overridden by the workflow. under_review is a real non-terminal hold state.
  • For 429, wait the number of seconds in Retry-After before retrying. Retry network failures and temporary 5xx responses with bounded exponential backoff, while reusing the same Idempotency-Key.

Keep your assistant honest

The API requires workflow_id on every session and rejects a per-request methods array — models trained on older API shapes sometimes invent use_case or products parameters. If the generated code passes those, it predates the workflow model; re-point the assistant at the docs above.

Next steps

On this page