---
title: "Identity verification integration"
description: "Create a hosted identity verification session from your server, send the customer to the link, and read the outcome by status read or signed event."
canonical_url: "https://noxtica.com/docs/identity-verification-integration/"
markdown_url: "https://noxtica.com/docs/identity-verification-integration/index.md"
last_updated: 2026-10-27
published: 2026-10-24
section: "identity-verification"
section_title: "Identity Verification"
site: "Noxtica"
language: "en"
source: markdown
---

# Identity verification integration

This guide takes a backend engineer from a provisioned account to a working hosted identity verification flow. Your server creates a session, your customer completes the steps on a page hosted by Noxtica, and your server reads the outcome by a status read or a signed event. Your server never receives the customer's answers or capture directly.

Results are automated signals. You decide what each outcome means for your customer; Noxtica hands you the evidence and an audit trail.

## Availability

Identity verification has **Controlled availability**. A tenant needs the module provisioned by the account team, and the account team confirms what applies to your tenant. Steps described here as optional apply only when they are enabled for your organisation. See the [product page](https://noxtica.com/products/identity-verification/) for the product overview and the limits of the result.

## Choose how to integrate

Three routes lead to a verification. Pick by who builds the capture experience and who sends the link.

| Route | Who creates the link | Who builds the capture experience | Guide |
| --- | --- | --- | --- |
| Hosted session from your server | Your server, by API | Noxtica hosts the page; you send the customer to it | This page |
| Invitation from Backoffice | A person in your organisation, in Backoffice | Noxtica hosts the page | [Invitations and hosted verification settings](https://noxtica.com/docs/identity-verification-invitations/) |
| Direct API from your server | Not applicable | Your own application | [Self-hosted identity verification (direct API)](https://noxtica.com/docs/identity-verification-self-hosted/) |

All three need the module provisioned for your tenant and, where an API is involved, a server key with the identity permission. Hosted sessions and invitations also need a recorded legal basis and share the settings described in [Invitations and hosted verification settings](https://noxtica.com/docs/identity-verification-invitations/).

## How the flow works

1. Your server creates a session with your own opaque customer reference. The response carries a `sessionId` and a single-use link. Store the `sessionId` next to your customer record.
2. You send the customer to the link: a full-page redirect, a new tab, an email or message, or a QR code.
3. The customer completes the steps on the hosted page in their own browser.
4. Noxtica processes the submission. A finished capture currently waits in the review queue in your Backoffice (`review_pending`) until someone in your organisation records a decision.
5. Your server learns the outcome by reading the session status, or from a signed `kyc.session.completed` event. Read the status before you act on an event.
6. If you set a return link, the customer can go back to your site from the hosted page after a completed outcome. The return link is a convenience, not proof that verification succeeded.

## Before you start

Complete these once per environment:

- **Module provisioned.** Your account team provisions identity verification for your tenant. Until then every request below receives a plain `404`.
- **Hosted verification page configured.** In the Backoffice, open Identity Verification → KYC Invitations → Settings and fill in the business name, privacy notice link and purpose text the customer sees. The KYC pages show a readiness checklist that names anything still missing. Until the page is configured the customer sees a "Verification isn't ready yet" message.
- **Legal basis recorded by your organisation.** An authorised person in your organisation records the legal basis for hosted identity verification in the same Settings page. Noxtica does not choose one for you. Until it is recorded, session creation is refused.
- **A server key with the identity permission.** In the Backoffice, open API & SDK → Server API Keys and create a key with the "Verify identity documents (KYC)" permission ticked. A new key defaults to a narrower permission that cannot create sessions. The secret is shown once, so save it in your server secret store. Server keys start with `sk_prod_`.
- **Approved return hosts (optional).** If you want a return link, add the host names of your return pages under "Approved return destinations" in the same Settings page. Saving that page asks for fresh step-up verification.
- **An events endpoint (optional).** If you do not want to poll, register an HTTPS endpoint under Integrations → Webhooks, subscribe it to the identity events you need, and save the signing secret when it is shown. The secret is shown once.

### Keys and base address

A key's prefix tells you what it is: server keys start with `sk_prod_`. Call `https://collect.noxtica.com` with it. A key that is missing, malformed, unknown or revoked is refused with `401 invalid_api_key`.

- Keep each key in its own secret store entry.
- A signing secret belongs to one endpoint.
- The Identity Verification → KYC Verifications → Integration tab shows ready-to-copy samples.

## Create a session

Send the request from your server. Never place the server key in a browser or a mobile app.

```http
POST https://collect.noxtica.com/v1/kyc/sessions
Authorization: Bearer sk_prod_your_server_key
Content-Type: application/json
Idempotency-Key: your-retry-key
```

### Request fields

The body is a JSON object. Field names are exact: an unknown field is ignored without an error, so a misspelt name silently falls back to its default.

| Field              | Type    | Required | Notes                                                                                                                                                                                          |
| ------------------ | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subjectRef`       | string  | Yes      | Your own opaque reference for the customer, 1 to 256 characters. Use an identifier from your system, not a name or an email address. Status reads return it to you; events never carry it.     |
| `expiresInSeconds` | integer | No       | How long an unopened link stays valid: 300 to 604800, default 86400. A value outside the range is rejected with a `400`, not adjusted.                                                         |
| `returnUrl`        | string  | No       | An `https` address whose host exactly matches an approved return host, up to 2048 characters. Any other host is rejected with `return_url_not_allowed`.                                        |
| `purpose`          | string  | No       | Short text shown to the customer on the hosted page, up to 500 characters. When you omit it the default purpose from your hosted page settings is used.                                         |
| `idType`           | string  | No       | An optional hint for the document type. Leave it out unless your account team asks you to send it; the customer chooses on the page.                                                           |
| `hostedOptions`    | object  | No       | Per-session switches for optional steps. Leave it out to use your defaults. If you send it, send the complete object your account team supplies; a partial object is rejected with a `400`. |

The request has no field for language, webhook address, or customer contact details. The hosted page follows the customer's browser language, and you register webhook endpoints in the Backoffice.

### Response

A new session returns `200` with this shape:

```json
{
  "ok": true,
  "sessionId": "<uuid>",
  "url": "https://verify.noxtica.com/s/<token>",
  "expiresAt": 1790848845000,
  "requestId": "<request-id>"
}
```

- `sessionId` is a UUID. Store it with your customer record.
- `url` is the single-use link. It is returned once, in this response, and no later call returns it again. Treat it as a secret: do not log it and do not put it in an analytics address.
- `expiresAt` is a time in epoch **milliseconds**, not seconds.
- `requestId` identifies the request. Quote it when you contact support.

### Idempotency

`Idempotency-Key` is optional and must be 8 to 128 characters when present. Without it, every call creates a new session. With it, a retry that carries the same key returns the same `sessionId` with `"replayed": true` and **no** `url`. The key alone identifies the session: a different body under the same key returns the original session.

If a request times out and you never saw the link, you cannot recover it. Create a new session with a new key and let the old one expire.

### Examples

```bash
curl -sS https://collect.noxtica.com/v1/kyc/sessions \
  -H "Authorization: Bearer $NOXTICA_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"subjectRef":"cust_example","returnUrl":"https://merchant.example.com/verification/return"}'
```

```js
const response = await fetch('https://collect.noxtica.com/v1/kyc/sessions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.NOXTICA_SERVER_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    subjectRef: 'cust_example',
    returnUrl: 'https://merchant.example.com/verification/return',
  }),
});

const body = await response.json();
if (!response.ok) throw new Error(`session creation failed: ${response.status} ${body.error}`);

// Store body.sessionId with your customer record, then send body.url to the customer.
```

```python
import json
import os
import urllib.request
import uuid

request = urllib.request.Request(
    "https://collect.noxtica.com/v1/kyc/sessions",
    data=json.dumps(
        {
            "subjectRef": "cust_example",
            "returnUrl": "https://merchant.example.com/verification/return",
        }
    ).encode(),
    headers={
        "Authorization": "Bearer " + os.environ["NOXTICA_SERVER_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    method="POST",
)

with urllib.request.urlopen(request) as response:
    body = json.load(response)

# Store body["sessionId"] with your customer record, then send body["url"] to the customer.
```

## Send the customer to the link

Choose the delivery that fits your journey:

- **Redirect.** Send the customer's browser to `url` as a full page. This is the simplest path.
- **New tab.** Open `url` in a new tab or window from your own page.
- **Email or message.** Send the link through your own channel.
- **QR code.** Show the link as a QR code so the customer can continue on a phone. The hosted page also offers a QR code and a copy-link option for moving to a phone.

What the hosted page does and does not do:

- **It cannot be embedded.** The hosted page refuses to load inside an iframe. Open it as a full page or in a new tab.
- **It does not message your page.** The page does not post a message to the window that opened it, and it does not close itself. Do not wait for a script on your side to learn the outcome.
- **It follows the customer's language.** The language comes from the customer's browser. You cannot set it, and the link accepts no parameters beyond its own path.
- **Some browsers cannot be used.** Some in-app browsers are not supported. The page tells the customer how to continue in their regular browser.

### Return link

The return link is optional.

- Set `returnUrl` when you create the session. Its host must be on your approved return hosts.
- After a completed outcome, the hosted page shows a plain "Return to your business" link to exactly that address. There is no automatic redirect, and no query parameters are added, so the address carries no session id or status.
- The link is not shown after a review, unverified, error, cancelled or expired outcome.
- Invitations created from the Backoffice carry no return link.

Because a finished capture currently waits for a decision, most customers do not reach a return link straight away. Design your journey so that it does not depend on the customer coming back: show your own "we will let you know" state, and finish through the status read or the event. Correlate with the `sessionId` you stored, and never treat a visit to your return page as proof of completion.

### Lifetimes

An unopened link stays valid until its own expiry, one day by default. After the customer first opens the link, the customer has 30 minutes to finish, or until the original expiry if that comes sooner. A session that waits in `review_pending` does not expire while it waits for a decision. [KYC Hosted Sessions](https://noxtica.com/docs/kyc-hosted-sessions/) explains expiry and the `410` responses the hosted page itself returns.

## Get the outcome

Two routes lead to the outcome: a status read, and a signed event. Use both. The event saves you from polling, and the status read is the authority.

### Read the session status

```http
GET https://collect.noxtica.com/v1/kyc/sessions/{sessionId}
Authorization: Bearer sk_prod_your_server_key
```

A session you created always returns `200`, including an expired one, which reads `"status": "expired"`. A session id that does not exist, or that belongs to another organisation, returns `404`.

```json
{
  "ok": true,
  "session": {
    "id": "<uuid>",
    "status": "review_pending",
    "subjectId": "cust_example",
    "expiresAt": 1790763900000,
    "completedAt": 1790762445000,
    "verificationId": "<verification-id>",
    "reasonCodes": ["<reason-code>"],
    "returnUrl": "https://merchant.example.com/verification/return",
    "decision": {
      "outcome": "review_pending",
      "reasonCodes": ["<reason-code>"],
      "decidedAt": 1790762445000
    }
  },
  "requestId": "<request-id>"
}
```

- Everything sits under `session`. `session.id` is the `sessionId` from creation, and `subjectId` is the `subjectRef` you sent.
- Timestamps are epoch milliseconds, or `null` when the moment has not happened.
- `decision` appears once the session has an outcome. Its `outcome` is `approved`, `rejected`, `review_pending` or `error`.
- `verificationId` identifies the verification case, so you can find it in the KYC pages of the Backoffice.
- When Noxtica Verification IDs are enabled for your organisation, the session also carries `nvid`.
- The response can carry more fields than this page lists. Ignore fields you do not use and do not fail on unknown ones.

Poll with a delay that grows between reads and stop at a final status. Do not poll in a tight loop; rate limits apply.

### Statuses

| Status              | Meaning                                                                                                                   | Event                                                                       |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `pending`           | Created. The customer has not opened the link.                                                                            | None                                                                        |
| `opened`            | The customer opened the link and is working through the steps.                                                            | None                                                                        |
| `verifying`         | The customer submitted and processing is running.                                                                         | None                                                                        |
| `review_pending`    | Processing finished and a decision is needed in your Backoffice review queue. Not a rejection, and not an approval.       | None yet. `kyc.session.completed` follows when a decision is recorded.      |
| `completed`         | A decision approved the session.                                                                                          | `kyc.session.completed` with status `completed`                             |
| `unverified`        | Processing finished with a negative outcome.                                                                              | `kyc.session.completed` with status `unverified`                            |
| `error`             | Processing could not finish, because of a fault or a refusal.                                                             | `kyc.session.completed` with status `error`                                 |
| `expired`           | The link or the capture window lapsed before a decision.                                                                  | None. Read the status to observe expiry.                                    |
| `cancelled_by_user` | The customer chose to cancel on the hosted page. This applies when cancelling is enabled for your organisation.           | `kyc.session.cancelled`                                                     |

Only `completed` is an approval. Treat every other status as not approved until you have read a later one.

## Receive events

Events are notifications. They save you from polling, and they are not the decision.

| Event                    | When it is sent                                                                                                                                      |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kyc.session.completed`  | A session reaches `completed`, `unverified` or `error`. This includes the moment a reviewer decision resolves a `review_pending` session.             |
| `kyc.session.cancelled`  | The customer cancelled on the hosted page. Subscribe to it explicitly. It applies when cancelling is enabled for your organisation.                  |

Other identity event types exist. This page covers the two that describe a hosted session; [Identity verification events and webhooks](https://noxtica.com/docs/identity-verification-webhooks/) lists every identity event, its payload and when it is sent.

Review decisions are relayed on a periodic schedule, so allow a few minutes between a decision and its event. An expired session sends no event: use the status read to observe expiry.

The Backoffice "Send test" button sends a sample of a different event type. It cannot produce an identity event, so test your handler with a signed sample of your own.

Every delivery is an HTTPS `POST` with these headers:

| Header                | Value                                                                  |
| --------------------- | ---------------------------------------------------------------------- |
| `X-Noxtica-Signature` | `sha256=` followed by the lowercase hex signature                      |
| `X-Noxtica-Timestamp` | The time of this attempt, in epoch milliseconds                        |
| `X-Noxtica-Event`     | The event type                                                         |
| `X-Noxtica-Delivery`  | The delivery id, identical to the `id` in the body                     |

The body is a JSON envelope with `id`, `type`, `created_at`, `tenant_id` and `data`. `schemaVersion` sits inside `data`, not on the envelope.

```json
{
  "id": "<sessionId>:<webhookId>",
  "type": "kyc.session.completed",
  "created_at": 1790762445000,
  "tenant_id": "<tenant-id>",
  "data": {
    "schemaVersion": "1",
    "sessionId": "<sessionId>",
    "status": "completed",
    "verificationId": "<verification-id>",
    "reasonCodes": ["<reason-code>"],
    "occurredAt": 1790762445000,
    "deliveryId": "<sessionId>:<webhookId>"
  }
}
```

```json
{
  "id": "<sessionId>:<webhookId>",
  "type": "kyc.session.cancelled",
  "created_at": 1790762300000,
  "tenant_id": "<tenant-id>",
  "data": {
    "schemaVersion": "1",
    "sessionId": "<sessionId>",
    "status": "cancelled_by_user",
    "cancelReason": "not_now",
    "cancelledAt": 1790762300000,
    "occurredAt": 1790762300000,
    "deliveryId": "<sessionId>:<webhookId>"
  }
}
```

- `cancelReason` is `null` or one of `not_now`, `privacy_concern`, `no_document`, `technical_problem` and `other`.
- When Noxtica Verification IDs are enabled for your organisation, a completed event also carries `data.nvid`. Other optional steps can add fields to `data`, so ignore fields you do not recognise.
- Events never carry your `subjectRef`. Match an event to your customer by `sessionId`.

### Verify the signature

The signature is an HMAC-SHA256 with your endpoint's signing secret over the timestamp, a dot, and the raw request body: `${timestamp}.${rawBody}`. Verify it before you parse or trust anything in the body.

- Use the body exactly as received. Do not parse and re-serialise it.
- Compare in constant time.
- Check the timestamp header, not `created_at`. Every retry is signed again with a fresh timestamp.
- Noxtica does not enforce a timestamp window on its side, so enforce your own. Five minutes is a sensible tolerance.

```js
import { createHmac, timingSafeEqual } from 'node:crypto';

const TOLERANCE_MS = 5 * 60 * 1000;

export function verifyNoxticaWebhook({ secret, timestamp, signature, rawBody, now = Date.now() }) {
  const sentAt = Number(timestamp);
  if (!Number.isSafeInteger(sentAt) || Math.abs(now - sentAt) > TOLERANCE_MS) return false;

  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  const provided = String(signature).replace(/^sha256=/, '').toLowerCase();

  const expectedBytes = Buffer.from(expected);
  const providedBytes = Buffer.from(provided);
  return expectedBytes.length === providedBytes.length && timingSafeEqual(expectedBytes, providedBytes);
}
```

```python
import hashlib
import hmac
import time

TOLERANCE_MS = 5 * 60 * 1000

def verify_noxtica_webhook(secret: str, timestamp: str, signature: str, raw_body: bytes) -> bool:
    if not (timestamp.isascii() and timestamp.isdigit()):
        return False
    if abs(time.time() * 1000 - int(timestamp)) > TOLERANCE_MS:
        return False

    signed = timestamp.encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    provided = signature.removeprefix("sha256=").lower()
    return hmac.compare_digest(expected, provided)
```

### Delivery, retries and duplicates

- A `2xx` response marks the delivery as delivered.
- Any `4xx` response is final. The delivery is not retried, including `408` and `429`. Return a `4xx` only when you mean it.
- A redirect, a `5xx` response, a timeout or a network error is retried, up to five more attempts. After that the delivery is marked failed and is not sent again. The retry spacing is not published, so do not build on it.
- Each attempt waits up to five seconds for your response, and redirects are not followed. Acknowledge quickly and process afterwards.
- Delivery is at least once. Deduplicate on `X-Noxtica-Delivery`, which stays the same across retries.
- Order is not fixed. Two events for the same session can arrive in either order.

### A notification is not authority

When an event arrives, verify it, record the delivery id, and then read `GET /v1/kyc/sessions/{sessionId}`. Act on the `status` that read returns, not on the event body. This keeps your state correct when events are late, repeated or out of order, and it means a forged or replayed event cannot approve anyone.

## Noxtica Verification ID

When Noxtica Verification IDs are enabled for your organisation, a session that completes or waits for review is given an ID in the form `NVID-XXXX-XXXX-XXXX-XXXX`. The customer sees it on the final page, and you read it from `session.nvid` or `data.nvid`. The ID is not a credential and does not sign anyone in. A person who has it can check its status on the verification site. That public lookup accepts requests only from the verification site itself, so your server cannot call it: read the ID from the session status or the event instead.

## Errors

Every error body is JSON and includes a `requestId`. Quote it when you contact support.

| Status | Error                                     | Meaning                                                                                              | What to do                                                                                                   |
| ------ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `400`  | `invalid_request`                         | Malformed JSON, a missing or over-long `subjectRef`, a bad `expiresInSeconds`, `purpose` or `Idempotency-Key`. | Fix the request. Check field names and limits.                                                               |
| `400`  | `invalid_hosted_options`                  | `hostedOptions` is not a complete, valid object.                                                     | Send the complete object or leave it out.                                                                    |
| `400`  | `invalid_id_type`                         | `idType` is not accepted for this session.                                                           | Leave `idType` out.                                                                                          |
| `400`  | `return_url_not_allowed`                  | `returnUrl` is not `https` or its host is not an approved return host.                               | Add the host in the hosted page settings or leave `returnUrl` out.                                           |
| `400` or `403` | an error ending in `_not_enabled`, or `email_verification_unavailable` | You asked for an optional step that is not enabled for your organisation.       | Leave that step out of `hostedOptions`, or ask your account team.                                            |
| `401`  | `invalid_api_key`                         | The key is missing, malformed, unknown or revoked.  | Check the `Authorization` header, and that the key matches the environment you call.                         |
| `403`  | `API key does not have write:identity scope` | The key lacks the identity permission.                                                            | Create a key with "Verify identity documents (KYC)" ticked.                                                  |
| `403`  | `kyc_legal_basis_receipt_required`        | Your organisation has not recorded the legal basis for hosted identity verification.                 | Record it in the Backoffice, then retry.                                                                     |
| `404`  | `V1 endpoint not found`                   | The module is not provisioned for your tenant, or the path is wrong. A status read also returns it for an unknown session id. | Check the path and session id. If both are right, ask your account team. Do not retry in a loop. |
| `413`  | `body_too_large`                          | The request body is too large.                                                                       | Send a small JSON object.                                                                                    |
| `415`  | `invalid_request`                         | The `Content-Type` is not JSON.                                                                      | Send `Content-Type: application/json`.                                                                       |
| `429`  | `Rate limit exceeded`                     | Too many requests.                                                                                   | Back off and retry. Honour `retryAfter` or `Retry-After` when present.                                       |
| `500`  | `server_misconfigured`                    | A server-side configuration problem.                                                                 | Contact support with the `requestId`.                                                                        |
| `503`  | `tenant_module_gate_unavailable`, `kyc_legal_basis_receipt_unavailable`, `invalid_request` with a `reason` | A temporary fault.                                       | Retry with a growing delay, and honour `Retry-After` when present.                                           |
| `503`  | `hosted_verify_not_configured`            | The hosted verification service is not set up for this environment.                                  | Contact your account team.                                                                                   |

A status read can also return `400` with `Invalid sessionId` when the id cannot be decoded.

The shared error envelope, rate limits and key handling are described in [Errors](https://noxtica.com/docs/api/errors/) and [Authentication](https://noxtica.com/docs/api/authentication/).

## Who it fits

Use identity verification where your policy requires identity evidence and a person on your team owns the review.

- **Onboarding and account opening.** Collect supported identity evidence through a hosted flow, without building your own capture screens.
- **Exceptions and review queues.** Separate finished, expired, cancelled, and needs-review cases (see [Statuses](https://noxtica.com/docs/identity-verification-integration/#statuses)). Keep the exception path visible to your operators.
- **Deciding when to ask.** Pair verification with Device Intelligence and Risk Scoring to decide which journeys call for identity evidence. [Common questions](https://noxtica.com/docs/identity-verification-integration/#common-questions) explains how they differ.

## How teams connect

Identity verification has three surfaces: the server API, the hosted page, and the console with events. [Choose how to integrate](https://noxtica.com/docs/identity-verification-integration/#choose-how-to-integrate) shows which route fits your team. Operators set up the hosted page in [Invitations and hosted verification settings](https://noxtica.com/docs/identity-verification-invitations/), and your endpoints subscribe to session events as described in [Identity verification events and webhooks](https://noxtica.com/docs/identity-verification-webhooks/).

## Limits

Read these boundaries before you design the flow.

- **Controlled availability.** What applies depends on an eligible identity configuration, your jurisdiction, and your contract. Confirm with your account team before you plan around it. See [Availability](https://noxtica.com/docs/identity-verification-integration/#availability).
- **Not legal identity proof.** No result is legal identity proof outside the scope of the configured provider and contract. Results are probabilistic and can include false positives and false negatives. They are not statements of fact. Read the [disclaimer](https://noxtica.com/disclaimer/).
- **You decide.** A qualified person on your team should review adverse decisions. Noxtica is not a consumer reporting agency. Do not use output for credit, insurance, employment, or housing eligibility.
- **Your responsibilities.** You own the notices, consents, and legal basis for collecting identity evidence from your customers in each jurisdiction you serve. Plan a fallback for sessions that expire, are cancelled, or end in an error. The [Privacy Policy](https://noxtica.com/privacy/) and the [Security page](https://noxtica.com/docs/security/) describe how Noxtica handles personal data; no module-specific data-scope statement for identity verification is published yet.

## Common questions

Answers about statuses, review, links, keys, events and the Verification ID are in [Identity verification FAQ and troubleshooting](https://noxtica.com/docs/identity-verification-faq/). Two product questions are not covered there.

### Can I turn identity verification on myself?

No. It is provisioned per tenant under Controlled availability; see [Availability](https://noxtica.com/docs/identity-verification-integration/#availability). Contact your account team to discuss your use case and jurisdiction.

### How does it relate to Device Intelligence and Risk Scoring?

They answer different questions. Device and risk evidence describe a browser context and an interaction. Identity verification gathers evidence about a person for your decision. Device risk can support identity evidence, but neither replaces the other.

## Go-live checklist

- The readiness checklist in the Backoffice KYC pages shows no missing item, and a test session runs end to end.
- The legal basis is recorded by an authorised person in your organisation, and the notices and consents you owe your customers are in place.
- The server key has the identity permission, lives in a server secret store, and is never sent to a browser or an app.
- Your server creates every session, sends an `Idempotency-Key`, stores the `sessionId` with the customer, and never logs the link.
- Your return page makes no assumption about the outcome, and your journey works when the customer never comes back.
- Your events endpoint uses HTTPS, verifies the signature over the raw body, enforces a timestamp tolerance, deduplicates on the delivery id, answers quickly and reads the session status before it acts.
- A scheduled status read covers sessions that send no event, such as an expired session or one in `review_pending`.
- Your code handles every status. Only `completed` is an approval, and `review_pending` is neither an approval nor a rejection.
- A person in your organisation reviews any adverse decision before it affects a customer. Results can be wrong in either direction and are not legal proof of identity.
- Noxtica does not delete verification details or evidence automatically and sets no retention date for them: they stay until a tenant administrator deletes them. [Verification data: storage, access and deletion](https://noxtica.com/docs/identity-verification-data/) explains how an administrator deletes a verification in Backoffice.
- The [Privacy Policy](https://noxtica.com/privacy/) and the [Security page](https://noxtica.com/docs/security/) describe how Noxtica handles personal data. This page does not.

## Related reading

- [Invitations and hosted verification settings](https://noxtica.com/docs/identity-verification-invitations/) — the Backoffice settings every hosted link uses, and invitations without code.
- [Self-hosted identity verification (direct API)](https://noxtica.com/docs/identity-verification-self-hosted/) — the direct API as it works today.
- [Identity verification events and webhooks](https://noxtica.com/docs/identity-verification-webhooks/) — every identity event and its payload.
- [Noxtica Verification ID (NVID)](https://noxtica.com/docs/identity-verification-id/) — the shareable reference for a verification.
- [Identity verification FAQ and troubleshooting](https://noxtica.com/docs/identity-verification-faq/) — answers to the questions integrators ask most.
- [KYC Hosted Sessions](https://noxtica.com/docs/kyc-hosted-sessions/) — session lifetime, expiry and the `410` responses.
- [Backend Integration](https://noxtica.com/docs/backend-integration/) — keep the credential on your server and handle explicit response states.
- [Identity verification](https://noxtica.com/products/identity-verification/) — the product overview, availability and limits.
