# Working with clientKey

`clientKey` is a user-defined identifier that you assign yourself and use to link KYC sessions with your users, applications, or contracts.

With `clientKey` you can:

* find all KYC sessions for a specific user;
* associate verification results with your objects (userId, orderId, loanId, etc.);
* filter sessions through the REST API;
* more easily process webhooks.

Important: the value of `clientKey` is **defined by you** — the platform does not generate it automatically.

***

## Format and limitations

* Type: `string`
* Maximum length: **36 characters**
* Format: any string, recommended to use Latin letters, digits, separators (`[a-zA-Z0-9_-]`)

Recommended usage:

* `clientKey = userId` — if one KYC check per user;
* `clientKey = "${userId}:${applicationId}"` — if KYC is linked to an application, order, contract;
* `clientKey = UUID` — if it is more convenient to work only with a KYC identifier.

***

## Two forms of `clientKey`: raw and encrypted

For WebSDK integration, it is important to understand that there are:

1. **Raw value (`clientKeyRaw`)**\
   The value you define: e.g., `"user_123"` or `"loan-42"`.
2. **Encrypted value (`clientKey`)**\
   The value you actually pass to the widget.\
   It is the result of encrypting `clientKeyRaw` using the scenario's secret key from the dashboard.

Workflow structure:

1. On your **backend**:
   * generate `clientKeyRaw` (up to 36 characters);
   * encrypt it with the scenario secret key;
   * send the **encrypted** `clientKey` to the frontend.
2. On the **frontend**:
   * pass the encrypted `clientKey` into `KYCWidget.setupKYC(...)`.
3. On the **KYC service** side:
   * the value is decrypted;
   * raw `clientKeyRaw` is stored in the session;
   * you can filter sessions and get results by it via API and webhooks.

> In the API (`/kyc/session/status`, `/kyc/sessions/filter` and webhooks) you work with the **raw** value you defined. Encryption is needed only for secure transfer of `clientKey` to the browser.

***

## Where `clientKey` is used

### In WebSDK

Widget call:

```js
window.KYCWidget.setupKYC({
  schemaId:  "SCHEMA_ID",            // Verification schema ID
  clientKey: "ENCRYPTED_CLIENT_KEY", // encrypted clientKeyRaw
  theme: "light",
  closeCb:   () => console.log("CLOSE"),
  successCb: (payload) => {
    console.log("SUCCESS", payload);
    // here you already know the raw clientKeyRaw since you generated it on backend
  },
});
```

### In REST API

* `/kyc/session/status` — you can pass `clientKey` to check the status for the specific key;
* `/kyc/sessions/filter` — there is a `clientKey` filter to get all sessions by this key.

Here you pass the **raw** value (`clientKeyRaw`), without encryption.

***

## Generating and encrypting `clientKey` on backend

The scenario secret key is stored only on your server. It must never be sent to the browser.

General idea:

1. Take `clientKeyRaw` (up to 36 characters).
2. Take scenario secret key (`scenarioSecretKey`) from the dashboard.
3. Encrypt `clientKeyRaw` on backend (example — AES-256-CBC).
4. Encode the result in Base64 and pass it to WebSDK as `clientKey`.

### Node.js example (AES-256-CBC)

```js
const crypto = require("crypto");

// 1. Raw identifier
const clientKeyRaw = "user_123"; // up to 36 chars

// 2. Scenario secret key (DO NOT send to browser!)
const password = "SCENARIO_SECRET_KEY";

// 3. Derive a 32-byte key
const key = crypto.createHash("sha256").update(password).digest().subarray(0, 32);

// 4. Generate 16-byte IV
const iv = crypto.randomBytes(16);

// 5. Encrypt clientKeyRaw
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
const encrypted = Buffer.concat([cipher.update(clientKeyRaw, "utf8"), cipher.final()]);

// 6. IV + ciphertext → Base64
const encryptedBase64 = Buffer.concat([iv, encrypted]).toString("base64");

console.log("clientKeyRaw:", clientKeyRaw);
console.log("clientKey (encrypted):", encryptedBase64);
```

Use this encrypted `clientKey` in:

```js
window.KYCWidget.setupKYC({
  schemaId:  "SCHEMA_ID",
  clientKey: encryptedBase64,
  // ...
});
```

> The “Check” button next to the scenario secret key in the dashboard uses the same algorithm and helps verify that encryption/decryption works correctly. For production always encrypt on your server.

***

## How to test `clientKey`

1. In the dashboard, find the scenario secret key.
2. Click “Check” and enter a test `clientKeyRaw` (e.g., `test_user_1`).
3. The dashboard will return the encrypted value — you can temporarily use it in integration examples.
4. Ensure that:
   * the widget opens without errors;
   * in your KYC sessions logs, the raw `clientKeyRaw` is visible (e.g., through filtering via `/kyc/sessions/filter`).

***

## Common questions and issues

### “Where do I get `clientKey` and `clientUser` values?”

* `clientKey` — **any string up to 36 chars** that you define: `userId`, application number, order ID, any useful value. This parameter is **required**.
* `clientUser` — also any string up to 36 chars, **but in new integrations you should avoid using it**.

> Best practice: set `clientKeyRaw` equal to your `userId` or `orderId`, encrypt it and pass only `clientKey`.

***

### Error: “Failed to perform asymmetric decryption!”

This usually means:

* a **raw** `clientKeyRaw` was passed to the widget instead of an encrypted value;
* or a **wrong scenario secret key** was used;
* or `clientKey` was corrupted or truncated (e.g., copy/paste issue).

Check that:

1. You pass a proper Base64 encrypted value to the widget.
2. The scenario in the dashboard matches the secret key used for encryption.
3. You are not using the deprecated `clientUser` parameter.

***

### “How to decipher `clientKey` for a webhook?”

You **do not need** `clientKey` to decrypt webhook data:

* webhook payload is encrypted with the webhook secret, not the `clientKey`;
* to decrypt you only need your webhook secret and the encrypted field from the POST.

`clientKey` inside the webhook JSON is simply the raw value you defined (`clientKeyRaw`).

***

### “Why is the webhook field `encrypted` always different even for the same JSON?”

This is expected:

* encryption uses **random IV (initialization vector)**;
* same JSON + same key = different ciphertext every time;
* after decryption the JSON is identical.

***

### “How is the general `success/failed` status determined?”

Overall KYC session status:

* `success` — **only if all checks pass**;
* if any check fails (OCR, fraud, databases, etc.) → overall status is `failed`.

External database checks (sanctions, Court Enforcement Office, etc.) also affect the overall status. Any critical red flag results in `failed`.

***

## Recommended workflow with `clientKey`

1. On backend at the beginning of KYC process:
   * generate or choose `clientKeyRaw` (e.g., `user_123`);
   * encrypt it → `encryptedClientKey`;
   * save `clientKeyRaw` on your side together with userId/orderId.
2. On frontend:
   * pass `encryptedClientKey` to `KYCWidget.setupKYC(...)`.
3. To obtain results:
   * receive webhook and inspect `clientKey` inside decrypted JSON;
   * or call `/kyc/sessions/filter` with `clientKey = clientKeyRaw`.

This ensures you always know which user or application each KYC session belongs to.
