stile
Getting Started

Authentication

How Stile API keys work — publishable vs. secret, sandbox mode, rate limits, and zero-downtime key rotation.

Every request to the Stile API is authenticated with an API key. This page covers the two key types and what each is allowed to do, how sandbox mode lets you build and test, the rate limits that apply per key, and how to rotate a key without downtime.

Key types

Stile uses two types of API keys, each with a specific purpose:

Key typePrefixWhere to usePurpose
Publishable keystile_pk_...Frontend (safe to expose)Used by the widget's legacy publishable-key mode to create sessions from the browser — deprecated; see Publishable keys and session creation.
Secret keystile_sk_...Server only (never expose)Full API access — create sessions, manage webhooks, read events, and verify webhook signatures.

Create and manage your keys in the dashboard.

Anatomy of a key

Every key encodes its role in the prefix, so you can tell what a key is — and what it's allowed to touch — at a glance, in code review or in a log line:

SegmentMeaning
stile_All Stile keys share this prefix — easy to grep for and to wire into secret scanners.
pk / skThe role: publishable key (frontend-safe) or secret key (server-only).
Everything afterThe secret itself. For secret keys it's shown once at creation — store it immediately.

Keep secret keys on your server

Secret API keys (stile_sk_...) grant full access to your organization. Never commit them to source control, include them in frontend code, or log them. Store them in environment variables. Publishable keys (stile_pk_...) are safe to include in your HTML.

Sandbox mode

Every organization is live by default — there's no separate test key. To build and test without running real verifications, enable sandbox mode on a dedicated testing organization (an org-level setting; turn it on from your dashboard org settings or by contacting support).

In a sandbox organization:

  • skip_verification: true instantly mints a verified session — no camera, ID, or liveness check — fires real webhooks, and issues a VP token. See the testing guide.
  • Sessions are unbilled, and a webhook endpoint is not required.
  • Usage is capped at 500 verifications per calendar month; requests over the cap return 402 test_quota_exceeded.

A live (non-sandbox) organization runs real identity verification, rejects skip_verification (400 parameter_invalid), and requires at least one active webhook endpoint before sessions can be created (the API returns 400 webhook_required otherwise) — this ensures verification results are always confirmed server-side.

Free tier

The first 100 verifications per calendar month are free (100/month by default, per organization). Beyond that, usage is billed via prepaid credits. Failed verifications are never charged and don't count against the free allotment.

Making API requests

Pass your secret key in the Authorization header as a Bearer token. With the Node.js SDK, set the key once — it's sent automatically with every request.

curl https://api.stile.id/v1/verification_sessions \
  -H "Authorization: Bearer stile_sk_YOUR_API_KEY" \
  -H "Content-Type: application/json"
import Stile from "@stile/node";

const stile = new Stile(process.env.STILE_API_KEY!);

Requests with a missing, malformed, or revoked key return 401 with an authentication_error of code api_key_invalid. See the error handling guide for the full error envelope.

You may not need the API directly

If you're using the widget with a publishable key, you don't need to make API calls yourself — the widget handles session creation automatically. The API is for advanced use cases like server-side session management, reading events, or managing webhook endpoints programmatically.

Publishable keys and session creation

Publishable keys can create verification sessions directly from the browser, but this path is deprecated — responses carry a Stile-Deprecation: publishable-key-session-create header, and organizations can be migrated to backend-created sessions (after which the call returns 403 publishable_session_create_blocked). Prefer the widget's session-url mode, where your server creates the session with a secret key and hands the client_secret to the widget.

If you do create sessions with a publishable key from the browser, a Cloudflare Turnstile captcha_token is required on the request — this defends the pk-only flow against stolen-key replay. Requests without it fail with 400 captcha_required. Secret keys don't need it.

Rate limits

Requests are rate-limited per API key on a per-minute rolling window — 1,000 requests/minute for secret keys and 100/minute for publishable keys (which are browser-exposed).

Every response includes rate limit headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1741564920   # Unix timestamp of window reset

When you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait. Retry after that interval with exponential backoff — the error handling guide has a reference implementation.

Rotating keys

Rotation is a create-then-revoke flow, so both keys stay valid while you roll over — there's no forced cutover and no downtime.

Create a replacement key

In the dashboard, create a new key of the same type (publishable or secret) as the one you're replacing. The old key keeps working until you revoke it.

Store the new secret

The secret is only shown once at creation — copy it into your secrets manager immediately.

Deploy with the new key

Update your environment variables (e.g. STILE_API_KEY) and redeploy your application.

Revoke the old key

Once all traffic is on the new key, revoke the old one in the dashboard. Any straggler requests still using it will fail with 401 api_key_invalid — a useful signal that something wasn't redeployed.

Next steps

On this page