Website Redirect
The website redirect is the lightest way to add identity verification to a web platform. Instead of embedding capture components, you send the user to the IDV Platform hosted verification app, they complete the flow there (selfie + identity document), and they are redirected back to your site with the result. Your backend receives the verified customer data through a callback.
The same hosted app can also be embedded as an iframe instead of a full-page redirect — the setup is nearly identical and both are covered below.
When to use it
- You want verification live quickly, with minimal front-end work.
- You don't need to embed capture inside your own branded UI (for that, use Web Components or the Mobile SDK).
- A browser-based journey is acceptable, with the hosted app handling capture, quality guidance, and processing.
Prerequisites
Verification runs against your registered configuration. You set most of it yourself in Integration Settings → Web redirect, per environment:
| Value | Purpose |
|---|---|
verifiedUrl | Where the user is redirected after a successful verification. |
rejectedUrl | Where the user is redirected when the result is rejected/failed. |
unverifiedUrl | Where the user is redirected when they cancel. |
callbackUrl | Where the platform POSTs the sensitive customer data on success (your backend). Set on the API interface tab. |
logoUrl | Your logo, shown in the hosted app (SVG for light background recommended; PNG supported). |
The redirect flow must also be enabled with the Enable web redirect toggle on the same tab.
From Innovatrics you receive, per environment, your OAuth2 client credentials (also visible in Integration Settings), the token endpoint, the API service URL, the hosted app URL, and the signing secret used to verify the callback.
The OAuth2 client secret must never live in front-end code — public front-ends would expose it and allow your account to be abused. Your backend authenticates, obtains the token, and calls the API. The front-end only performs the redirect.
How it works
At a high level, the data flows like this:
- The user starts verification on your website.
- Your website asks your backend for a session token.
- Your backend obtains an access token (OAuth2 client-credentials) and calls the IDV Platform API to create a session.
- The API returns a
sessionToken; your backend passes it back to the website. - The website sends the user to the hosted verification app (redirect) or embeds it (iframe), passing the
sessionToken. - The user completes the verification (selfie + identity document) in the hosted app.
- On success, the API POSTs the sensitive customer data to your
callbackUrl; your backend stores it and acknowledges with204 No Content. - The hosted app returns the user to your site with the result — as query parameters (redirect) or a
postMessage(iframe).
Integration steps
1. Get an access token (backend)
Authentication uses the same OAuth2 model as the rest of the IDV Platform (see Capture & Workflow API → Authentication). Your backend requests an access token with the client-credentials grant, using the client credentials provisioned in Integration Settings. Keep the token server-side; it is short-lived, so cache it and refresh it before expiry.
The token endpoint is OpenID Connect standard and expects a form-encoded body, not JSON:
const response = await fetch(`${TOKEN_URL}/protocol/openid-connect/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
}),
});
const { access_token, expires_in } = await response.json();
Access tokens are short-lived. Cache the token for expires_in seconds rather than for a fixed period, refresh it before it expires, and retry once on a 401.
2. Create a session (backend)
Exchange the access token for a session token, optionally specifying the locale for the hosted app.
const response = await fetch(`${SERVICE_URL}/api/v1/session`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${access_token}`,
},
body: JSON.stringify({
configuration: { locale: 'en' },
}),
});
const { sessionToken } = await response.json(); // return this to the front-end
3. Send the user to the hosted app (front-end)
Redirect flow — navigate the browser to the hosted app with the session token:
window.location.href = `${APP_URL}/?sessionToken=${sessionToken}`;
Iframe flow — embed the hosted app instead, adding viewType=iframe:
const iframe = document.createElement('iframe');
iframe.src = `${APP_URL}?sessionToken=${sessionToken}&viewType=iframe`;
iframe.width = '100%';
iframe.height = '100%';
iframe.allow = 'camera';
iframe.style.border = 'none';
document.body.appendChild(iframe);
4. The user verifies
In the hosted app the user provides a selfie and a photo of their identity document. The app uploads and processes them, then returns the outcome.
Getting the result
The overall outcome is delivered to the front-end, and the sensitive customer data is delivered to your backend.
Outcome (front-end)
Both flows return the same result: a single result field that is success or error.
-
Redirect flow — the user returns with the result as a query parameter. The user may not land on the page they started from.
https://your-site.com/verified?result=success -
Iframe flow — the result is delivered via
postMessageto the page that started verification. The message body is a JSON string, so parse it. Always validate the message origin:window.addEventListener('message', (event) => {if (event.origin !== 'https://your-site.com') return;let payload;try {payload = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;} catch {return; // not our message}const isSuccess = payload.result === 'success';});Guard the parse: other scripts on the page also post messages, and they will not all be JSON.
Which URL the user lands on. A successful verification returns to verifiedUrl. Every other ending — rejected, failed, cancelled, or abandoned — currently returns to unverifiedUrl; rejectedUrl is configured and stored but not yet selected separately. Drive your messaging from the result parameter rather than from the landing page.
The result parameter and the postMessage body both pass through the user's browser and must be treated as a display hint only. Confirm every verification server-side from the callback below before granting access or creating an account.
Customer data (backend callback)
Before the user is redirected back, the platform POSTs the verified customer data to your callbackUrl. Your backend must verify the signature, store the data securely, and respond 204 No Content to acknowledge receipt.
This callback is specific to the hosted verification flow: it carries one event type and the full verified customer data. It is not the same as the record-change webhooks, which use a different envelope and signature header. A tenant can use both.
The event has a event type (currently VERIFICATION_SUCCEEDED) and a data payload:
| Field | Description |
|---|---|
sessionToken | Identifier of the session. |
result | Overall status — always VERIFIED for this event. |
timestamp | Verification timestamp (for integrity checks). |
customer.fullName | Full name of the verified user. |
customer.dateOfBirth | Date of birth. |
customer.placeOfBirth | Place of birth. |
customer.address.fullAddress | Full address. |
customer.documentNumber | Document number of the verified ID. |
customer.dateOfExpiry | Document expiry date. |
customer.personalNumber | Personal or national identification number. |
customer.firstName · customer.lastName | Given name and surname, parsed separately. |
customer.sex | Sex as printed on the document. |
customer.document_type | Type of the presented document. |
customer.document_issuing_country · customer.document_nationality | Issuing country and nationality. |
customer.selfie | Base64-encoded selfie. Sent by default; can be switched off per tenant. |
verification_flow | selfie_id or selfie_only — which capture path ran. |
type VerificationSucceededResult = {
customer: {
address: { fullAddress?: string };
documentNumber?: string;
personalNumber?: string;
fiscalNumber?: string;
dateOfBirth?: string;
dateOfExpiry?: string;
firstName?: string;
lastName?: string;
fullName?: string;
placeOfBirth?: string;
sex?: string;
document_type?: string;
document_issuing_country?: string;
document_nationality?: string;
selfie?: string;
};
timestamp: number;
result: string; // 'VERIFIED'
sessionToken?: string;
verification_flow?: 'selfie_id' | 'selfie_only';
};
Fields are added over time, so ignore unknown properties rather than rejecting the payload — a strict schema will start failing when the next field ships.
Verifying the callback signature
Each delivery carries an HMAC-SHA256 digest of the raw request body in the Authorization header, computed with the signing secret issued for your tenant. Verify it before acting on the payload:
const { createHmac, timingSafeEqual } = require('node:crypto');
// rawBody must be the bytes as received — not a re-serialized object.
const expected = createHmac('sha256', WEBHOOK_SECRET_KEY)
.update(rawBody, 'utf8')
.digest('hex');
const received = req.headers.authorization ?? '';
const ok =
expected.length === received.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(received));
if (!ok) return res.status(401).end();
Capture the raw body before any JSON middleware parses and re-serializes it — re-serializing changes key order and whitespace, and the digest will never match.
Deliveries can repeat. Key your processing on sessionToken so a duplicate is a no-op rather than a second account.
Error handling
Errors surface as HTTP status codes and are handled inside the hosted app; you only need to handle errors during app initialization and while retrieving the result. For security, the hosted app does not expose error details — each error carries a unique tracing ID. If you hit an error, contact Innovatrics with that tracing ID for analysis.
See also
- Web Components — embed capture in your own web UI instead of redirecting
- No-code / Low-code
- Webhooks & Callbacks
- Integration Overview