How to Add Identity Verification to Your App in Minutes
Learn how to create a Stile identity verification session, add the hosted widget, verify signed webhooks, and launch a privacy-first flow.
Adding identity verification to an app should not require building document capture, biometric liveness, face matching, mobile driver's license support, and webhook security from scratch.
With Stile, the production integration has three parts:
- Your server creates a verification session.
- Stile's hosted widget guides the user through the required checks.
- A signed webhook tells your server whether the session was verified.
This guide implements that complete loop with Stile's HTTP API and a small amount of JavaScript. No Stile server SDK is required.
What you will build
By the end of this guide, your application will be able to:
- create an identity verification session from your backend;
- present Stile's hosted verification experience in your app;
- support the verification methods selected in your Stile workflow, including mDL, document capture, selfie liveness, and face match where applicable;
- receive and authenticate a signed verification result;
- bind that result to the correct user or transaction; and
- test the full flow without verifying a real user.
The security boundary matters
The browser can start verification and update the interface, but only your server should grant access, release an order, approve a user, or mark a transaction verified.
Before you start
You need:
- a Stile account;
- a secret API key beginning with
stile_sk_; - a published Stile workflow with an ID beginning with
wf_; - an authenticated backend endpoint in your application; and
- a public HTTPS endpoint for Stile webhooks.
You do not need OAuth credentials, a client ID and secret pair, or a provider-specific server library. Stile authenticates server requests with a Bearer API key, and the public HTTP API works with any backend language.
How the Stile integration works
A production Stile integration separates three trusted boundaries.
Your browser
The browser renders the hosted <stile-frame> component. The widget handles the user-facing verification flow without sending your secret API key to the client.
Your server
Your server authenticates the user, selects a published workflow, creates the Stile session, and stores the returned session ID against the user, order, account change, delivery, or other action being verified.
Your webhook handler
Stile sends a signed event when the session changes state. Your webhook handler verifies the signature, matches the Stile session to your local record, and applies the result once.
That last step is the source of truth. A browser redirect or stile:verified event is useful for interface feedback, but it is not proof that your backend should trust.
Step 1: Publish a verification workflow
A Stile workflow defines the policy for every session that uses it. Configure:
- the use case;
- target jurisdictions;
- accepted verification methods;
- liveness and face-match requirements;
- returning-user preferences; and
- session expiry or review behavior.
For an identity verification flow, the workflow might offer a mobile driver's license when supported, with physical document capture as another path. Selfie liveness and face match are layers that run alongside a primary proof method rather than methods on their own.
Copy the published workflow ID from the Stile dashboard. Add it and your secret key to the server environment:
Keep all three values out of client-side code. Do not prefix them with NEXT_PUBLIC_ or place them in a browser bundle.
Verification policy lives in the workflow, not the request
workflow_idis required on every session you create, and amethodsarray cannot be combined with it. The published workflow controls which methods run, and Stile resolves them for the applicable use case and jurisdiction.
Step 2: Create a verification session on your server
Create an authenticated endpoint such as POST /api/identity-verification/start. The endpoint should load the current user and the local action being verified from your database, then call Stile.
A few details are important:
type: "identity"creates an identity verification session. Usetype: "age"for an age verification flow, ortype: "student"for a student check.workflow_idis required, and it selects the policy you published in Stile.client_reference_idshould identify the user or transaction in your system. Stile echoes it in webhook events.Idempotency-Keymakes a retry return the original session instead of creating a duplicate. The replay carries anIdempotency-Replayed: trueresponse header, and reusing the same key with a different body is rejected with a 400 rather than silently resolved.client_secretis short-lived and intended for the widget. Return it to the browser, but never log it.- Persist
session.idbefore the user completes verification.
Derive identity and ownership from your authenticated server session. Do not accept an arbitrary email, user ID, workflow ID, or transaction ID from the browser without validating it.
Live sessions require a webhook endpoint first
Outside sandbox, Stile refuses to create a session for an organization with no enabled webhook endpoint: the call fails with a 400 and a
webhook_requirederror code. Register the endpoint in Step 4 before you switch a live key on.
Step 3: Add the hosted Stile widget
Load Stile's public widget script and point the component at the backend endpoint you created:
The widget sends a POST to session-url with a small JSON body of hints (workflowId, email, jurisdiction), and expects JSON back containing session_id and client_secret. Note the shape difference: the Stile API returns the session as id, and your endpoint remaps it to session_id. Returning methods and age_tier as well lets the widget render the real step rail instead of falling back to a wallet-only one. Your server authenticates the request, creates the session, and returns only those short-lived fields.
The hosted experience can guide the user through the methods compiled from the workflow. Depending on the workflow and jurisdiction, that can include:
- an ISO/IEC 18013-5 mobile driver's license from a supported wallet;
- physical identity-document capture; and
- selfie liveness and biometric face match layered on top of the primary method.
No app download is required for the hosted browser flow.
You may listen for the widget's stile:verified event to show a completion state. Do not use that client-side event to mark the user verified. Its own payload says so: the event detail ships with serverConfirmed: false. Show a confirming state while your frontend waits for your own backend to process the signed webhook.
Step 4: Verify Stile's signed webhook
Add an HTTPS endpoint in the Stile dashboard and subscribe to verification_session.verified. Save the one-time signing secret as STILE_WEBHOOK_SECRET.
Stile sends the signature in this format:
There is no X- prefix on that header name. The v1 value is a lowercase hexadecimal HMAC-SHA256 signature over:
The request body must remain raw until after signature verification. With Express, register this route before express.json():
The event envelope is { id, object: "event", type, created, data: { object } }, where data.object is the verification session.
Your application-specific markVerificationCompleteOnce operation should use one database transaction to:
- claim
event.idunder a unique constraint; - find a pending local record whose Stile session ID matches
session.id; - confirm its local ID matches
session.client_reference_id; and - transition that record to verified exactly once.
Return 2xx after the event is safely persisted or queued. Return 400 for an invalid signature. Return 5xx for a temporary processing failure so Stile can retry: delivery is attempted up to five times with an escalating backoff, and a 4xx other than 429 is treated as permanent and not retried.
A valid signature proves Stile sent the event. Matching the session ID and client_reference_id proves the result belongs to the action your application is about to approve.
Step 5: Test the full flow in sandbox
Use a dedicated Stile sandbox organization for development and automated tests. The API base URL and key format stay the same.
In sandbox only, add skip_verification: true to create a session that moves directly to verified and fires a real signed webhook:
Use an HTTPS tunnel such as ngrok or Cloudflare Tunnel to expose a local webhook handler. Then test:
- valid and invalid signatures;
- duplicate webhook delivery;
- a mismatched session ID or
client_reference_id; - retries with the same idempotency key;
- unauthenticated session-creation requests; and
- a browser completion event arriving before the webhook.
Remove skip_verification before using a live organization. Live sessions reject it with a 400.
What your application receives
Stile returns the proof your application needs to make a decision, including the session status, verification method, resolved jurisdiction, age tier when applicable, and audit metadata.
The public integration does not send identity-document images to your application. Captured document evidence is purged once a verification or review reaches a final decision, including rejections. One-way hashed anchors can persist for deduplication and returning-user checks, along with the verification outcome.
Proof with privacy
Your application gets a signed result without becoming another store of ID images, selfies, or raw document data. That reduces the amount of sensitive identity material in your systems while preserving the evidence needed to make and audit a business decision.
Production checklist
Before launch, confirm that:
The complete Stile integration
The full production loop is simple:
- Create a published verification workflow.
- Create a session from your server with
POST /v1/verification_sessions. - Pass the session to Stile's hosted widget.
- Let the user complete the required identity checks.
- Verify the signed webhook and bind it to your local transaction.
- Apply the result once.
That is enough to add document verification, mDL support, liveness, face match, and a signed identity result to an existing application without building a new identity-data pipeline.
Create a Stile account, follow the production quickstart, or review the Verification Sessions API reference.
Share this article