# Zyphe Developer Documentation
> Technical documentation for integrating Zyphe APIs, SDKs, and webhooks.
This file contains all documentation content in a single document following the llmstxt.org standard.
## Quickstart
This is the shortest path from zero to a **reviewed verification result**. Follow the seven steps
in order. Each links to the relevant guide if you want more depth. You can complete the whole
path in the **sandbox**, with no contract and no production data.
:::tip[Where to go for detail]
This page is deliberately linear and opinionated. Every capability it touches has a full guide;
follow the links when you need options, edge cases, or reference material.
:::
## 1. Create an account and open the sandbox
Sign up and open your organization in the [dashboard](/docs/guides/dashboard). Switch the
dashboard to **sandbox** mode. Everything below runs against synthetic data, so you can build and
test without any production entitlement or data-processing agreement.
## 2. Create your first flow
A **flow** is the sequence of steps a user goes through to get verified (document check, liveness,
proof of address, and so on). Open the flow builder and create one. Start from a template or add
a couple of steps such as **Document Verification** and **Liveness**.
→ [Create a flow](/docs/guides/create-a-flow)
## 3. Run a test verification
From the flow you just built, start a verification request in the sandbox and complete it yourself.
This produces a **flow result**: the record you will review in step 6.
→ [Create a flow → running a verification](/docs/guides/create-a-flow)
## 4. Choose how you integrate: public link or SDK
You have two ways to put the flow in front of a real user (see
[Choosing an integration method](/docs/choosing-an-integration-method) for a full comparison,
including MCP agents):
- **Public link**: Zyphe hosts the verification UI; you share a URL and receive the result. The
fastest path to a working integration.
→ [Sharing a flow](/docs/guides/create-a-flow/sharing)
- **SDK**: embed verification directly in your web or mobile app for a native experience and full
control over the UX.
→ [SDKs](/docs/guides/sdk)
Pick one for now; you can add the other later.
## 5. Receive a webhook
Register a webhook endpoint so your backend is notified when a verification completes, instead of
polling. Zyphe signs every delivery so you can verify it came from us.
→ [Webhooks](/docs/guides/webhook) · [Webhook signature](/docs/guides/webhook/webhook-signature)
## 6. Review the result
Open the flow result in the dashboard. You will see its status, the collected data (subject to
your [role](/docs/guides/manage-users) and [PII access](/docs/pii-access-control)), and any
computed [scores](/docs/guides/scoring). Approve or reject it, or configure
[manual review](/docs/guides/manual-review) to route results to a reviewer automatically.
→ [Manual review](/docs/guides/manual-review) · [Reviewing scores](/docs/guides/scoring/reviewing-scores)
## 7. Prepare for production
When your sandbox integration works end to end, use the full
**[Sandbox and go-live](/docs/guides/sandbox-and-go-live)** checklist. In short:
1. Generate production [API keys](/docs/guides/api-keys).
2. Point your webhook at your production endpoint and confirm signature verification.
3. Review [billing](/docs/guides/billing) and enable the products you need.
4. Switch your integration from the sandbox to the production environment.
5. Map statuses and failure reasons with [Statuses and codes](/docs/reference/statuses-and-codes).
## Where to next
- Finish QA with [Sandbox and go-live](/docs/guides/sandbox-and-go-live).
- Compare integration options in [Choosing an integration method](/docs/choosing-an-integration-method).
- Read short product explainers under [How it works](/docs/how-it-works).
- Add continuous compliance with [AML monitoring](/docs/aml) and
[Transaction Monitoring](/docs/transaction-monitoring).
- Let partners reuse an existing verification with
[reusable credentials / one-click KYC](/docs/reusable-credentials-one-click-kyc).
- Automate flow setup with the [MCP server](/docs/guides/model-context-protocol-mcp).
- Explore the full [API reference](/docs/openapi/zyphe-sdk).
---
## @zyphe-sdk/browser
Install the package:
## Embedding the Zyphe Verification Iframe
Use the `startVerificationSession` function to embed a Zyphe verification flow in your web app:
```ts
const container = document.getElementById('your-container-id')
const flowParams = {
// Provide at least one identity field: email or a credential.
email: 'user@example.com',
credentials: [
{ type: 'EXTERNAL_ID', externalId: 'user_123' },
{ type: 'WALLET', address: '0x123...', chain: 'ETHEREUM' },
],
flowId: 'your-flow-id',
isSandbox: true, // or false for production
customData: {/* optional custom data */}, // Optional: pass any extra data needed for your flow
}
const opts = {
apiKey: 'your-api-key',
// ...other SDK options
}
const result = await startVerificationSession({
containerElement: container,
flowParams,
opts,
eventHandlers: {
onSuccess: (message) => {
// Handle success
console.log('Verification completed successfully')
console.log('Completed step:', message.flowStep)
console.log('Verification request session:', message.vrSession)
},
onFailure: (message) => {
// Handle failure
console.log('Verification failed')
console.log('Failure details:', message.error)
},
},
})
if (result.error) {
// Handle error (e.g., show a message to the user)
console.error('Failed to start verification session:', result.error)
} else {
// Session created successfully, iframe embedded
console.log('Verification session started successfully')
}
```
### Key Features
- **Non-throwing API**: The function returns a **Result** object: `{ error, data }`. If session creation fails, `error` will be set and no iframe will be embedded.
- **Automatic iframe management**: Creates and appends an iframe to the specified container with proper styling and permissions.
- **Event handling**: Listen for success and failure events from the verification flow, including the completed flow, flow step, and verification request session context.
- **Camera permissions**: The iframe is automatically configured with camera permissions for document and identity verification.
- **Identity credentials**: Start a session with `email`, an `EXTERNAL_ID` credential, a `WALLET` credential, or any combination. At least one is required.
:::warning Email aliases not supported in production
The `email` field in `flowParams` does not accept email aliases (e.g. `user+tag@example.com`) in production environments. Use a plain email address without `+alias` suffixes when `isSandbox` is `false`.
:::
---
## API Reference
### `startVerificationSession({ containerElement, flowParams, opts, eventHandlers })`
Creates a verification session and embeds the Zyphe iframe in the specified container.
#### Parameters
- **containerElement**: `HTMLElement` - The DOM element to append the iframe to
- **flowParams**: `InitializeZypheFlowParams` - The parameters for the verification flow
- `email?`: `string` - User's email address. Required only when `credentials` does not include an external ID or wallet.
- `credentials?`: `VerificationIdentifierCredential[]` - User credentials. Include `{ type: 'EXTERNAL_ID', externalId: 'user_123' }` and/or `{ type: 'WALLET', address: '0x123...', chain: 'ETHEREUM' }`.
- `flowId`: `string` - The ID of the verification flow
- `flowStepId?`: `string` - Optional. If omitted, the next incomplete step is selected automatically.
- `isSandbox`: `boolean` - Whether to use sandbox or production environment
- `customData?`: `any` - Optional custom data to pass to the flow
- **opts**: `SDKOptions` - SDK configuration options (e.g., `{ apiKey }`). Optional theming overrides are also accepted. See [Theming](../dashboard/theming.mdx) for the full list.
- **eventHandlers?**: `{ onSuccess?, onFailure? }` - Optional event handlers
- `onSuccess?`: `(message: ZypheVerificationSuccessMessage) => void` - Called when verification completes successfully
- `onFailure?`: `(message: ZypheVerificationFailureMessage) => void` - Called when verification fails
#### Verification event payloads
The browser SDK listens to the iframe `postMessage` events emitted by the Zyphe verification flow. Both success and failure handlers receive the full event payload.
```ts
type ZypheVerificationSuccessMessage = {
type: 'zyphe-verification-success'
flow: Flow
flowStep: FlowStep
vrSession: {
customData?: VerificationRequest['customData']
email?: string
verificationRequestId?: string
}
}
type ZypheVerificationFailureMessage = {
type: 'zyphe-verification-failure'
flow: Flow
flowStep: FlowStep
vrSession: {
customData?: VerificationRequest['customData']
email?: string
verificationRequestId?: string
}
error?: string | { code?: string; message?: string }
}
```
- `flow`: The flow where the verification event happened.
- `flowStep`: The flow step that completed or failed.
- `vrSession`: Verification request session context, including `verificationRequestId`, `email`, and `customData`.
- `error`: Failure details. This field is only present on `zyphe-verification-failure` events.
#### Returns
`Promise<{ error?: any, data?: any }>` - A Result object
- If `error` is present, session creation failed and no iframe is embedded
- If `data` is present, session was created and iframe is embedded
#### Example
```ts
const result = await startVerificationSession({
containerElement: document.getElementById('verification-container'),
flowParams: {
email: 'user@example.com',
credentials: [{ type: 'EXTERNAL_ID', externalId: 'user_789' }],
flowId: 'flow_123',
isSandbox: true,
customData: { userId: 'user_789' },
},
opts: { apiKey: 'your-api-key' },
eventHandlers: {
onSuccess: (message) => {
console.log('Verification successful', message.flowStep.type)
},
onFailure: (message) => {
console.log('Verification failed', message.error)
},
},
})
```
---
## Browser Compatibility
This SDK requires a modern browser with support for:
- ES6+ features
- Web Crypto API (for future webhook signature verification)
- Iframe elements
---
## Get Next Step
The Get Next Step endpoint allows integrators to programmatically determine the current progress of a verification flow and identify the next action required from the user.
## Endpoint Details
- **Method:** `GET`
- **Path:** `/sdk/flow/{flow_id}/vr/next-step`
- **Authentication:** API Key (`x-api-key` header)
### Query Parameters
| Parameter | Type | Required | Description |
| :--------------- | :------ | :------- | :---------------------------------------------------------------------------------------------- |
| `sandbox` | boolean | Yes | Whether to query the sandbox environment. |
| `email` | string | No | The email address of the user undergoing verification. |
| `credentialType` | string | No | Use `EXTERNAL_ID` or `WALLET` when querying by credential. |
| `externalId` | string | No | Required when `credentialType=EXTERNAL_ID`. |
| `address` | string | No | Wallet address. Required when `credentialType=WALLET`. |
| `chain` | string | No | Wallet chain. Required when `credentialType=WALLET` (for example `ETHEREUM`, `SOLANA_TESTNET`). |
| `locale` | string | No | Optional response locale. |
Provide at least one identity field: `email`, `credentialType=EXTERNAL_ID` with `externalId`, or `credentialType=WALLET` with `address` and `chain`.
## Response
The endpoint returns a `GetNextStepResponse` object.
```json
{
"nextStep": {
"id": "step_123",
"name": "Document Verification",
"type": "DV",
"order": 1,
"config": { ... }
},
"isCompleted": false,
"executedActions": ["action_abc"]
}
```
### Field Descriptions
- **nextStep:** A `FlowStep` object containing the details of the next step. Returns `null` if all steps are completed.
- **isCompleted:** A boolean indicating if the entire verification flow is finished.
- **executedActions:** An array of workflow actions that have already been executed for this user.
## How It Works
The system evaluates the current state of the verification flow for the provided email or credential:
- If the user has not started, it returns the first step in the flow.
- If the user has completed steps, it identifies the next incomplete step based on the flow configuration.
- If all steps are finished, `isCompleted` is set to `true` and `nextStep` is `null`.
## Use Cases
This endpoint is primarily used for custom UI implementations where the integrator wants to manage the user experience programmatically rather than using the Zyphe hosted UI. It provides the necessary metadata to render the appropriate component for each step (e.g., DV, Form, POA, SPID).
## Example
```bash
curl -X GET "https://api.zyphe.com/sdk/flow/flow_123/vr/next-step?sandbox=true&email=user@example.com" \
-H "x-api-key: your_api_key_here"
```
Query by external ID:
```bash
curl -X GET "https://api.zyphe.com/sdk/flow/flow_123/vr/next-step?sandbox=true&credentialType=EXTERNAL_ID&externalId=user_123" \
-H "x-api-key: your_api_key_here"
```
Query by wallet:
```bash
curl -X GET "https://api.zyphe.com/sdk/flow/flow_123/vr/next-step?sandbox=true&credentialType=WALLET&address=0x123...&chain=ETHEREUM" \
-H "x-api-key: your_api_key_here"
```
---
## SDK Integration
We provide various npm packages to help you integrate the Zyphe platform into your application.
## @zyphe/sdk-browser
https://www.npmjs.com/package/@zyphe-sdk/browser
This package is designed for browser applications. It provides a set of utilities to interact with the Zyphe platform, including webhook signature verification and verification session management.
## @zyphe/sdk-node
https://www.npmjs.com/package/@zyphe-sdk/node
This package is designed for Node.js applications. It provides a set of utilities to interact with the Zyphe platform, including webhook signature verification and verification session management.
## React Native Integration
For mobile applications, we provide a comprehensive integration guide using React Native with WebView. This approach works with both Expo and bare React Native projects. See the [React Native Integration](/docs/guides/sdk/react-native) guide for detailed setup instructions.
## @zyphe/sdk-core
https://www.npmjs.com/package/@zyphe-sdk/core
This environment agnostic package is used internally by both the `sdk-node` and `sdk-browser` packages. The exports are passed through to the `sdk-node` and `sdk-browser` packages, so you typically won't need to use this package directly. It can also be used in React Native backend servers.
## Theming
You can match the verification flow to your brand using dashboard themes and per-session SDK overrides. See the [Theming](../dashboard/theming.mdx) guide for the full list of color, border-radius, font, and logo options.
---
## @zyphe-sdk/node
Install the package:
## 1. Webhook Signature Verification
This package provides the core webhook signature verification logic for Zyphe. It enables secure verification of incoming webhook requests in Node.js using HMAC-SHA256 signatures.
### Basic Usage
```ts
const secretHex = ''
const rawBodyString = '...' // The raw request body as a string
const signatureHeader = 't=1234567890,v0=abcdef...' // The signature header from the webhook
const isValid = verifyWebhookSignatureHeader(secretHex, rawBodyString, signatureHeader)
if (isValid) {
// Signature is valid
} else {
// Signature is invalid
}
```
- `secretHex`: The hex-encoded secret key used to sign the webhook.
- `rawBodyString`: The raw request body as a string (must match exactly what was sent).
- `signatureHeader`: The signature header string, e.g. `t=1234567890,v0=abcdef...`.
- Returns: `true` if the signature is valid, or `false` otherwise.
### Advanced Utilities
- `parseSignatureHeader(signatureHeader)`, Parse the signature header into timestamp and signature.
- `hexToBytes(hex)` / `bufferToHex(buffer)`, Convert between hex and bytes.
- `timingSafeEqual(a, b)`, Constant-time string comparison.
- Error codes/messages for robust error handling.
---
## 2. Verification Session Management
You can programmatically create verification sessions and construct user-facing URLs for onboarding flows.
### Create a Verification Session
```ts
const response = await createVerificationSession(
{
// Provide at least one identity field: email or a credential.
email: 'user@example.com', // optional when credentials are provided
credentials: [
{ type: 'EXTERNAL_ID', externalId: 'user_123' },
{ type: 'WALLET', address: '0x123...', chain: 'ETHEREUM' },
], // optional when email is provided
flowId: '', // required
isSandbox: false, // required
customData: {
// optional
plan: 'pro',
// ...any extra data you want to pass
},
},
{
apiKey: '',
environment: 'staging', // or "production", "local"
handoffBaseUrl: 'https://your-app.com', // optional
// Theming, all optional. Light mode:
backgroundColor: '#ffffff', // page background
accentColor: '#155dfb', // buttons, icons, highlights
cardColor: '#ffffff', // card surface
// Dark mode:
darkBackgroundColor: '#16181d',
darkAccentColor: '#155dfb',
darkCardColor: '#16181d',
borderRadius: '12px', // shared radius for cards, buttons, inputs
// Deprecated (still honored): primaryColor → background, secondaryColor → accent
isFullscreen: true, // optional
},
)
```
- `email` and `credentials` identify the user. Provide at least one of `email`, an `EXTERNAL_ID` credential, or a `WALLET` credential.
- `credentials` (optional): Pass an array of credentials, for example `{ type: 'EXTERNAL_ID', externalId: 'user_123' }` or `{ type: 'WALLET', address: '0x123...', chain: 'ETHEREUM' }`.
- You can combine identity fields, such as `email` plus `externalId`, `email` plus `wallet`, or all three.
- `customData` (optional): Pass any extra data you want to associate with the verification session. This will be available in the webhook payload and session data.
- The function returns a Promise resolving to the API response. Handle errors as needed (e.g., try/catch or checking for error fields).
### Construct a Verification Session URL
```ts
const url = constructVerificationSessionUrl({
flowParams: {
flowId: '...',
email: 'user@example.com',
credentials: [{ type: 'EXTERNAL_ID', externalId: 'user_123' }],
isSandbox: false,
customData: { plan: 'pro' }, // optional
},
verificationSession: response.data, // from createVerificationSession
opts: {
apiKey: '',
environment: 'staging',
handoffBaseUrl: 'https://your-app.com', // optional
// Theming, all optional. Light mode:
backgroundColor: '#ffffff', // page background
accentColor: '#155dfb', // buttons, icons, highlights
cardColor: '#ffffff', // card surface
// Dark mode:
darkBackgroundColor: '#16181d',
darkAccentColor: '#155dfb',
darkCardColor: '#16181d',
borderRadius: '12px', // shared radius for cards, buttons, inputs
// Deprecated (still honored): primaryColor → background, secondaryColor → accent
isFullscreen: true, // optional
},
})
```
- The constructed URL includes all relevant parameters and can be used to embed or redirect users to the verification flow.
- Optional fields (like `handoffBaseUrl`, `accentColor`, `borderRadius`, etc.) will be appended as query parameters if provided. See [Theming](../dashboard/theming.mdx) for the full list of theme options and how they work.
#### Types
- `InitializeZypheFlowParams`: Parameters for initializing a verification flow.
- `SDKOptions`: Configuration for the SDK.
- `SdkCreateVerificationRequestResponse`: API response type for session creation.
#### Error Handling
- If session creation fails, the function may throw or return an error object depending on usage context. Always check for errors and handle them appropriately.
- Example:
```ts
try {
const response = await createVerificationSession(...);
if (response.error) {
// Handle error
console.error(response.error);
} else {
// Success
}
} catch (err) {
// Handle thrown errors (e.g., network issues)
console.error(err);
}
```
:::warning Email aliases not supported in production
The `email` field in `flowParams` does not accept email aliases (e.g. `user+tag@example.com`) in production environments. Use a plain email address without `+alias` suffixes when `isSandbox` is `false`.
:::
---
## 3. Complete Verification Steps
The Node SDK also includes helpers for completing document selection, document verification, and liveness steps from your backend.
Typical flow:
1. Create a verification session with `createVerificationSession`.
2. Check the current step with `getNextStepDetails` if you need to branch dynamically.
3. Complete document selection with `completeDocumentSelectionStep` when the next step is `document-selection`.
4. Upload document images with `completeDvStep` when the next step is a document verification step.
5. Complete liveness with `completeLivenessStep` when the flow requires liveness.
### Inspect the Next Step
```ts
const opts: SDKOptions = {
apiKey: '',
environment: 'staging',
}
const { error, data: nextStep } = await getNextStepDetails(
{
flowId: '',
email: 'user@example.com',
credential: { type: 'EXTERNAL_ID', externalId: 'user_123' },
isSandbox: true,
},
opts,
)
if (error) {
console.error(error)
} else {
console.log('Next step:', nextStep)
}
```
- Use `getNextStepDetails` to decide whether to ask for document selection, document images, liveness capture, or another step.
- Provide at least one identity field: `email`, an `EXTERNAL_ID` credential, or a `WALLET` credential.
- This helper calls the `sdk_get_next_step` endpoint documented in `docs/openapi/sdk-get-next-step.api.mdx`.
### Complete Document Selection and Document Verification
```ts
async function readBase64File(filePath: string) {
return (await readFile(filePath)).toString('base64')
}
const opts: SDKOptions = {
apiKey: '',
environment: 'staging',
}
const verificationSessionResponse = await createVerificationSession(
{
email: 'user@example.com',
flowId: '',
isSandbox: true,
},
opts,
)
if (verificationSessionResponse.error || !verificationSessionResponse.data) {
throw new Error('Failed to create verification session')
}
await completeDocumentSelectionStep(
{
verificationSession: verificationSessionResponse.data,
payload: {
country: 'IT',
documentType: DocumentType.IDENTITY_CARD,
},
},
opts,
)
const dvResult = await completeDvStep(
{
verificationSession: verificationSessionResponse.data,
countryCode: 'IT',
images: {
type: 'DOUBLE_SIDED_DOCUMENT',
frontImage: await readBase64File('./front.jpg'),
backImage: await readBase64File('./back.jpg'),
},
},
opts,
)
console.log('DV step completed')
console.log(dvResult)
```
- This example follows `../zyphe-sdk/examples/node-complete-step-dv/src/index.ts`.
- Use `DocumentType.IDENTITY_CARD`, `DocumentType.DRIVING_LICENSE`, or `DocumentType.PASSPORT` when selecting the document type.
- `completeDvStep` accepts either:
- `type: 'DOUBLE_SIDED_DOCUMENT'` with `frontImage` and `backImage`
- `type: 'SINGLE_SIDED_DOCUMENT'` with a single `image`
- Image values must be base64-encoded strings.
- The SDK returns the API response for the submitted document job, including the verification request ID and job ID.
### Complete a Liveness Step
```ts
async function readBinaryFile(filePath: string) {
return new Uint8Array(await readFile(filePath))
}
const opts: SDKOptions = {
apiKey: '',
environment: 'staging',
}
const verificationSessionResponse = await createVerificationSession(
{
email: 'user@example.com',
flowId: '',
isSandbox: true,
},
opts,
)
if (verificationSessionResponse.error || !verificationSessionResponse.data) {
throw new Error('Failed to create verification session')
}
const calibrationImage = await readBinaryFile('./calibration.jpg')
const video = await readBinaryFile('./liveness.mp4')
const movementImages = await Promise.all(['./movement-1.jpg', './movement-2.jpg'].map((filePath) => readBinaryFile(filePath)))
const livenessResult = await completeLivenessStep(
{
verificationSession: verificationSessionResponse.data,
frontImage: Buffer.from(calibrationImage),
video: Buffer.from(video),
movementImages: movementImages.map((image) => Buffer.from(image)),
},
opts,
)
console.log('Liveness step completed')
console.log(livenessResult)
```
- This example follows `../zyphe-sdk/examples/node-complete-step-liveness/src/index.ts`.
- `completeLivenessStep` first fetches the challenge configuration, uploads the required assets to the provided presigned URLs, and then calls the final liveness processing endpoint.
- For active liveness challenges, you must provide a `video` and the same number of `movementImages` requested by the challenge.
- `frontImage` is the calibration image uploaded before the final liveness submission.
---
## 4. Flow Results
You can use the Node SDK to list flow results for a flow and fetch a single flow result by ID.
### List Flow Results
```ts
const opts: SDKOptions = {
apiKey: '',
environment: 'staging',
}
const { error, data } = await listFlowResults(
{
organizationId: '',
flowSlug: '',
isSandbox: true,
pagination: {
take: 10,
skip: 0,
},
},
opts,
)
if (error) {
console.error(error)
} else {
console.log('Total flow results:', data?.totalCount ?? 0)
console.log('Current page:', data?.page ?? [])
}
```
- `organizationId`: The organization that owns the flow results.
- `flowSlug`: The flow slug used to scope the results.
- `isSandbox`: Set to `true` for sandbox data and `false` for production data.
- `pagination` (optional): Use `take`, `skip`, `sort`, and `filter` to control paging.
- `locale` (optional): Localize fields that support translated values.
- Returns: A Promise resolving to a paginated response with `totalCount` and a `page` array of flow results.
### Get a Flow Result
```ts
const opts: SDKOptions = {
apiKey: '',
environment: 'staging',
}
const { error, data: flowResult } = await getFlowResult(
{
organizationId: '',
flowResultId: '',
isSandbox: true,
},
opts,
)
if (error) {
console.error(error)
} else {
console.log('Flow result:', flowResult)
}
```
- `flowResultId`: The ID of the specific flow result you want to retrieve.
- Returns: A Promise resolving to the full flow result payload, including result collections such as `dvResults`, `poaResults`, and `amlResults` when available.
## 5. Types and Configuration
- `InitializeZypheFlowParams`, `SDKOptions`, `SdkCreateVerificationRequestResponse`, for type-safe integration.
- `Environments`, `EnvironmentConfigs`, `API_KEY_HEADER`, for environment and API configuration.
---
## React Native
This guide shows you how to integrate Zyphe verification into a React Native application using Expo or bare React Native.
## Overview
The React Native integration uses a WebView to display the Zyphe verification flow. You'll need:
- A backend server to create verification sessions securely
- A React Native app with WebView to display the verification flow
- Proper permissions configured for camera, microphone, and other required features
## Prerequisites
- Node.js 18+ installed
- React Native or Expo project set up
- Zyphe account with API credentials ([Get started here](https://dashboard.zyphe.com))
---
## Backend Setup
:::warning Security
**Never expose your API key in the mobile app.** Always create verification sessions on your backend server.
:::
You need to create a backend endpoint that generates verification session URLs securely. You can use any backend framework (Express, Fastify, NestJS, Hono, etc.).
Your backend service should:
1. **Authenticate the user** - Verify the user's identity using your authentication system (session, JWT, OAuth, etc.)
2. **Validate identity fields** - Ensure at least one of `email`, external ID, or wallet is provided
3. **Create the verification session** - Use the Zyphe SDK to generate a verification session
4. **Return the verification URL** - Send the URL back to the mobile app
5. **Handle errors appropriately** - Manage authentication failures and session creation errors
### Install the SDK
### Create Verification Session Endpoint
Create an API endpoint that receives the user's identity details and returns a verification URL:
```ts
createVerificationSession,
constructVerificationSessionUrl,
type SDKOptions,
type InitializeZypheFlowParams,
} from '@zyphe-sdk/core'
const PUBLISHABLE_API_KEY = 'your-api-key' // Your API Key
const FLOW_ID = 'your-flow-id' // The flow ID you want to use for creating the verification session
type CreateVerificationSessionInput = {
email?: string
externalId?: string
wallet?: {
address: string
chain: 'ETHEREUM' | 'ETHEREUM_TESTNET' | 'SOLANA' | 'SOLANA_TESTNET'
}
}
// Your API endpoint handler (adapt to your framework)
async function createVerificationSessionHandler(input: CreateVerificationSessionInput) {
if (!input.email && !input.externalId && !input.wallet) {
throw new Error('Provide at least one of email, externalId, or wallet')
}
const opts: SDKOptions = {
apiKey: PUBLISHABLE_API_KEY,
environment: 'production',
}
const flowParams: InitializeZypheFlowParams = {
email: input.email,
credentials: [
...(input.externalId ? [{ type: 'EXTERNAL_ID' as const, externalId: input.externalId }] : []),
...(input.wallet ? [{ type: 'WALLET' as const, address: input.wallet.address, chain: input.wallet.chain }] : []),
],
flowId: FLOW_ID,
isSandbox: true,
}
const { error, data: verificationSession } = await createVerificationSession(flowParams, opts)
if (error) {
throw new Error('Failed to create verification session')
}
const verificationSessionUrl = constructVerificationSessionUrl({
opts,
verificationSession,
flowParams,
})
return {
url: verificationSessionUrl,
sessionId: verificationSession.id,
}
}
```
`email` is optional when `credentials` includes an external ID or wallet. You can send any combination, such as email only, external ID only, wallet only, email plus external ID, or all three.
### Fullscreen Mode
By default, the verification UI displays as cards. To show the UI in fullscreen mode, add the `zypheFullscreen=true` query parameter to the verification URL:
```ts
const verificationSessionUrl = constructVerificationSessionUrl({
opts,
verificationSession,
flowParams,
})
// Add fullscreen mode
const fullscreenUrl = `${verificationSessionUrl}&zypheFullscreen=true`
```
This is particularly recommended for mobile WebView implementations to provide a better user experience.
---
## React Native App Setup
### Install Required Packages
```bash
npm install react-native-webview
# For Expo:
npx expo install react-native-webview expo-camera expo-constants
```
For bare React Native, follow the [react-native-webview installation guide](https://github.com/react-native-webview/react-native-webview/blob/master/docs/Getting-Started.md).
---
## Platform Configuration
### Android Permissions
#### Expo (Managed Workflow)
Add permissions to your `app.json` or `app.config.js`:
```json
{
"expo": {
"android": {
"permissions": [
"android.permission.INTERNET",
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO",
"android.permission.MODIFY_AUDIO_SETTINGS"
]
},
"plugins": [
[
"expo-build-properties",
{
"android": {
"usesCleartextTraffic": true
}
}
]
]
}
}
```
:::warning
The `usesCleartextTraffic` setting is only needed for development to allow HTTP connections to localhost. **Remove this for production builds** and use HTTPS endpoints only.
:::
#### Bare React Native
Edit `android/app/src/main/AndroidManifest.xml`:
```xml
```
#### Android Permissions Explained
- **INTERNET**: Required for all network communication
- **CAMERA**: Required for document scanning and selfie verification
- **RECORD_AUDIO**: Required for liveness detection and video recording
- **MODIFY_AUDIO_SETTINGS**: Required for audio settings during verification
---
### iOS Permissions
#### Expo (Managed Workflow)
Add permissions to your `app.json` or `app.config.js`:
```json
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "Camera access is required for identity verification and document scanning.",
"NSMicrophoneUsageDescription": "Microphone access is required for liveness detection during identity verification.",
"NSPhotoLibraryUsageDescription": "Photo library access allows you to upload existing photos for verification.",
"NSLocationWhenInUseUsageDescription": "Location access may be used for fraud prevention during verification."
}
}
}
}
```
#### Bare React Native
Edit `ios/YourAppName/Info.plist`:
```xml
NSCameraUsageDescriptionCamera access is required for identity verification and document scanning.NSMicrophoneUsageDescriptionMicrophone access is required for liveness detection during identity verification.NSPhotoLibraryUsageDescriptionPhoto library access allows you to upload existing photos for verification.NSLocationWhenInUseUsageDescriptionLocation access may be used for fraud prevention during verification.
```
#### iOS Permissions Explained
- **NSCameraUsageDescription**: Required for document capture and selfie verification
- **NSMicrophoneUsageDescription**: Required for liveness detection and video recording
- **NSPhotoLibraryUsageDescription**: Allows users to upload existing photos
- **NSLocationWhenInUseUsageDescription**: Optional but recommended for fraud detection
:::info
The description strings will be shown to users when iOS requests permission. Make them clear and specific to pass App Store review.
:::
---
## Implementation
### Basic Example
Here's a complete React Native component that implements Zyphe verification:
```tsx
const BACKEND_URL = 'http://localhost:3000' // Your backend URL
export default function App() {
const [email, setEmail] = useState('')
const [verificationUrl, setVerificationUrl] = useState('')
const [loading, setLoading] = useState(false)
const [showWebView, setShowWebView] = useState(false)
const createVerificationSession = async () => {
if (!email) {
Alert.alert('Error', 'Please enter your email')
return
}
setLoading(true)
try {
// Request camera permission before proceeding
const { status } = await Camera.requestCameraPermissionsAsync()
if (status !== 'granted') {
Alert.alert(
'Camera Permission Required',
'Camera access is required for identity verification. Please enable camera permissions in your device settings.',
)
setLoading(false)
return
}
const response = await fetch(`${BACKEND_URL}/api/create-verification-session`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
})
const data = await response.json()
if (response.ok && data.url) {
setVerificationUrl(data.url)
setShowWebView(true)
} else {
Alert.alert('Error', data.error || 'Failed to create verification session')
}
} catch (error) {
Alert.alert('Error', 'Failed to connect to the server. Make sure the backend is running.')
} finally {
setLoading(false)
}
}
const handleWebViewMessage = (event: any) => {
// Handle messages from the WebView if needed
console.log('WebView message:', event.nativeEvent.data)
}
const resetSession = () => {
setShowWebView(false)
setVerificationUrl('')
setEmail('')
}
if (showWebView && verificationUrl) {
return (
← BackVerification (
)}
/>
)
}
return (
Zyphe VerificationEnter your email to start the verification process
{loading ? : Start Verification}
Make sure your backend server is running
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
content: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
title: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 10,
textAlign: 'center',
color: '#333',
},
subtitle: {
fontSize: 16,
marginBottom: 30,
textAlign: 'center',
color: '#666',
},
input: {
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 15,
fontSize: 16,
marginBottom: 20,
},
button: {
backgroundColor: '#007AFF',
borderRadius: 8,
padding: 15,
alignItems: 'center',
},
buttonDisabled: {
backgroundColor: '#9cc9ff',
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: '600',
},
infoContainer: {
marginTop: 30,
padding: 15,
backgroundColor: '#e8f4ff',
borderRadius: 8,
},
infoText: {
fontSize: 14,
color: '#007AFF',
textAlign: 'center',
},
webViewHeader: {
flexDirection: 'row',
alignItems: 'center',
padding: 15,
backgroundColor: '#fff',
borderBottomWidth: 1,
borderBottomColor: '#ddd',
},
backButton: {
padding: 5,
},
backButtonText: {
fontSize: 18,
color: '#007AFF',
fontWeight: '600',
},
headerTitle: {
flex: 1,
fontSize: 18,
fontWeight: '600',
textAlign: 'center',
marginRight: 60,
color: '#333',
},
webView: {
flex: 1,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
})
```
---
## WebView Configuration
The WebView must be configured with specific properties for the verification flow to work properly:
```tsx
```
### Key Properties Explained
- **allowsInlineMediaPlayback**: Allows the camera/media to play inline instead of fullscreen (iOS)
- **mediaPlaybackRequiresUserAction**: Disables the requirement for user interaction before media plays
- **javaScriptEnabled**: Required for the verification flow to function
- **domStorageEnabled**: Required for storing session data and state
---
## Permission Handling
### Request Permissions at Runtime
It's recommended to request camera permissions before starting the verification flow:
#### Using Expo Camera
```tsx
const requestPermissions = async () => {
const { status } = await Camera.requestCameraPermissionsAsync()
if (status !== 'granted') {
Alert.alert('Permission Required', 'Camera access is required for verification.')
return false
}
return true
}
```
#### Using Bare React Native (Android)
```tsx
const requestAndroidPermissions = async () => {
if (Platform.OS !== 'android') return true
try {
const granted = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.CAMERA,
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
])
return (
granted['android.permission.CAMERA'] === PermissionsAndroid.RESULTS.GRANTED &&
granted['android.permission.RECORD_AUDIO'] === PermissionsAndroid.RESULTS.GRANTED
)
} catch (err) {
console.warn(err)
return false
}
}
```
---
## Testing
### iOS Simulator
Use the default localhost URL:
```tsx
const BACKEND_URL = 'http://localhost:3000'
```
### Android Emulator
Use the special Android emulator address:
```tsx
const BACKEND_URL = 'http://10.0.2.2:3000' // Points to host machine's localhost
```
### Physical Devices
For testing on physical devices, you'll need to:
1. Use your computer's local network IP address
2. Ensure both devices are on the same WiFi network
3. Configure firewall to allow connections on port 3000
```tsx
const BACKEND_URL = 'http://192.168.1.100:3000' // Replace with your local IP
```
Or use automatic IP detection with the `ip` package.
---
## Production Checklist
Before deploying to production:
- ✅ Remove `usesCleartextTraffic` from Android config
- ✅ Use HTTPS endpoints for all API calls
- ✅ Never expose API keys in the mobile app
- ✅ Test on real devices with production builds
- ✅ Verify all permission descriptions are clear and accurate
- ✅ Handle permission denials gracefully
- ✅ Add error handling for network failures
- ✅ Test the complete verification flow end-to-end
---
## Common Issues
### Camera Not Working in WebView
Make sure:
- All required permissions are granted
- `allowsInlineMediaPlayback={true}` is set
- `mediaPlaybackRequiresUserAction={false}` is set
- Camera permissions are declared in AndroidManifest.xml/Info.plist
### Backend Connection Errors
- iOS Simulator: Use `http://localhost:3000`
- Android Emulator: Use `http://10.0.2.2:3000`
- Physical Devices: Use your computer's local network IP
- Ensure backend server is running
### Permissions Not Requested
- Check that permission strings are in AndroidManifest.xml (Android) or Info.plist (iOS)
- Verify you're calling the permission request methods
- Check device settings to ensure permissions weren't permanently denied
---
## API Keys Management
API keys are used to authenticate requests made to the Zyphe API. They allow your applications to securely interact with our services, whether from a server-side environment or directly from a client-side application.
## Key Types
Zyphe provides two distinct types of API keys, each designed for specific use cases and security requirements:
### Publishable Keys
- **Prefix:** `zyphe_pk`
- **Use Case:** Intended for use in client-side code, such as web browsers or mobile applications (e.g., within the Zyphe Browser SDK).
- **Security:** These keys have limited permissions and can be restricted by allowed origins (CORS) to prevent unauthorized use on other domains.
### Secret Keys
- **Prefix:** `zyphe_sk`
- **Use Case:** Intended for use in server-side environments (e.g., Node.js, Python, or direct API calls from your backend).
- **Security:** Secret keys grant full access to your organization's API resources. They must never be exposed in client-side code or committed to public version control systems.
## Managing API Keys
You can manage your API keys through the Zyphe Dashboard. The following operations are available:
Open the API Keys page from the dashboard to create and manage API credentials.
- **Create:** Generate new keys for different environments or applications. You can specify a name, description, and key type.
- **View:** Access your existing keys. Note that for security reasons, the full value of a Secret Key is only displayed once at the time of creation.
- **Update:** Modify the metadata of a key, such as its name, description, or allowed origins.
- **Delete:** Revoke a key to immediately disable its access to the API.
When generating a key, choose its type and configure allowed origins for publishable keys.Generated keys appear in the API Keys list with their type, allowed origins, and actions.
## Security Best Practices
- **Protect Secret Keys:** Always store secret keys securely using environment variables or secret management services.
- **Restrict Origins:** For publishable keys, always configure the "Allowed Origins" setting to ensure the key can only be used from your authorized domains.
- **Rotate Keys:** Periodically rotate your API keys to minimize the impact of a potential credential leak.
- **Principle of Least Privilege:** Use different keys for different applications or environments to isolate access.
## Related Documentation
- [SDK Integration](/docs/guides/sdk)
- [Webhook Signature](/docs/guides/webhook/webhook-signature)
---
## Backwards Compatibility
## Legacy document-verification payload
Organizations migrating from an earlier Zyphe integration can opt in to the legacy `data.kyc` object. New integrations should consume the current `data.dv` object documented in [Webhook Payload](./webhook-payload).
:::warning Migration only
The legacy format is disabled by default and is available only for document-verification webhooks. Contact Zyphe support to enable or disable it for your organization.
:::
When enabled, the same webhook contains both representations:
```json
{
"data": {
"kyc": {
"kycInquiryId": "d72b7afc-6841-4478-9d21-3e8dd3d26695",
"identityId": "zid:example",
"status": "PASSED",
"processCode": "ProcessCompleted",
"processType": "info",
"processMessage": null
},
"dv": {
"verificationRequestId": "d72b7afc-6841-4478-9d21-3e8dd3d26695",
"flowId": "2d8285d7-f4ba-42df-ab3f-681d9870d37a",
"flowStepId": "03559a0f-be8e-4ab7-964d-1ff4745d92a4",
"identityEmail": "user@example.com",
"status": "PASSED",
"reasons": [],
"errorMessage": null
}
}
}
```
## Key differences
| Concept | Legacy `kyc` | Current `dv` |
| -------------------- | ---------------------------------------------- | --------------------------- |
| Verification request | `kycInquiryId` | `verificationRequestId` |
| Identity | `identityId` | `identityEmail` |
| Flow references | Not included | `flowId`, `flowStepId` |
| Scores | Multiple legacy score fields | `scoreBiometricSelfie` |
| Failure details | `processType`, `processCode`, `processMessage` | `reasons[]`, `errorMessage` |
| Tenant | `tenantId` | Not included |
## Migration
1. Read `data.dv` whenever it is present.
2. Update field mappings using the table above.
3. Test success, failure, and review events without reading `data.kyc`.
4. Contact support to disable the legacy format.
The outer webhook envelope is unchanged and is documented in [Receiving Webhooks](./webhook-structure).
---
## Custom Fields
Custom fields let you correlate a Zyphe result with records in your system without encoding business context into the webhook URL itself.
Add query parameters to the verification link you give the user:
```text
https://verify.zyphe.com//kyc/?uid=123&product=exchange
```
The values are returned in two places:
```json
{
"data": {
"dv": {
"customData": {
"uid": "123",
"product": "exchange"
}
}
},
"custom": {
"uid": "123",
"product": "exchange"
}
}
```
- `custom` is the flow-level value used for reconciliation and routing.
- `data..customData` is the custom-data snapshot attached to that verification request.
Use opaque identifiers rather than sensitive personal information in query parameters. URLs may appear in browser history, analytics, logs, and referrer headers.
For the surrounding envelope and step fields, see [Receiving Webhooks](./webhook-structure) and [Webhook Payload](./webhook-payload).
---
## Failure and Review Events
This page documents failure semantics. It intentionally shows only the fields relevant to handling failures; the complete field list for each result type lives in [Webhook Payload](./webhook-payload).
## How failures are represented
A failed verification normally has three layers of status:
- `event` describes this delivery, typically `FAILED`.
- `flowStatus` describes the overall flow after the step failed.
- `data..status` and its reason fields describe the individual result.
```json
{
"resultId": "7fd1c306-7830-4261-b372-181f742055ef",
"event": "FAILED",
"data": {
"dv": {
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"status": "FAILED",
"reasons": ["DOCUMENT_EXPIRED"],
"errorMessage": null
}
},
"custom": {},
"flowStatus": "FAILED"
}
```
## Failure detail by result type
| Result key | Failure fields |
| ------------- | ----------------------------------------- |
| `dv` | `reasons[]`, plus optional `errorMessage` |
| `poa` | Optional `reason` |
| `form` | Optional `reason` and `errorMessage` |
| `spid` | Optional `reason` and `errorMessage` |
| `phone` | Optional `reason` and `errorMessage` |
| `wallet` | Optional `reason` and `errorMessage` |
| `kyb` | Optional `reason` and `errorMessage` |
| `geolocation` | Optional `reason` and `errorMessage` |
Do not build logic around human-readable error text. Prefer the structured status and reason fields when present, and retain `errorMessage` for diagnostics.
## Failure before a result exists
A step can fail before its result record is created. In that case, identifying fields may contain empty or default values while request context and `errorMessage` remain available:
```json
{
"event": "FAILED",
"data": {
"dv": {
"id": "00000000-0000-0000-0000-000000000000",
"verificationRequestId": "d72b7afc-6841-4478-9d21-3e8dd3d26695",
"status": "",
"reasons": [],
"errorMessage": "Processing failed: timeout exceeded"
}
},
"flowStatus": "FAILED"
}
```
Treat the verification request and flow identifiers as the reliable correlation context in this case.
## Review events
When human review is required, Zyphe emits `REVIEW` instead of `FAILED`:
```json
{
"event": "REVIEW",
"data": {
"dv": {
"status": "REVIEW",
"reasons": []
}
},
"flowStatus": "REVIEW"
}
```
Do not mark the user as rejected when receiving `REVIEW`. Keep the result pending until a later webhook or API read provides the final decision. See [Manual Review](../manual-review) for the operator workflow and [Statuses and codes](/docs/reference/statuses-and-codes) for the full event and reason vocabulary.
---
## Webhook Integration
Webhooks notify your backend when verification results or lifecycle events change. Zyphe sends an HTTPS `POST` request to the webhook URL configured for the flow; your endpoint verifies the request, records or queues the event, and returns `200 OK`.
## Integration checklist
1. **Configure the endpoint.** Add your public HTTPS webhook URL in the flow settings in the Zyphe Dashboard.
2. **Keep the secret safe.** Store your organization's webhook secret on your server. Never expose it in browser or mobile code.
3. **Read the raw request body.** Signature verification must use the exact bytes received, before JSON parsing or re-serialization.
4. **Verify `X-Signature`.** Reject requests with an invalid signature or an unacceptable timestamp. See [Webhook Signature](./webhook-signature).
5. **Dispatch the event.** Standard result webhooks use `data`; notification webhooks use `eventData`. See [Receiving Webhooks](./webhook-structure).
6. **Acknowledge delivery.** Return `200 OK` promptly, then perform slower application work asynchronously.
7. **Make processing idempotent.** Delivery failures are retried, so receiving the same logical event more than once must be safe.
:::tip Test before going live
Use the webhook trigger and monitor tools in the flow's developer controls to inspect the payload, delivery attempts, response status, and errors before sending production traffic.
:::
## Choose the right reference
| You need to… | Read… |
| ----------------------------------------------------------------------------------- | -------------------------------------------------------- |
| Implement the HTTP endpoint and route events | [Receiving Webhooks](./webhook-structure) |
| Inspect fields for DV, PoA, Form, Phone, Wallet, SPID/CIE, KYB, AML, or Geolocation | [Webhook Payload](./webhook-payload) |
| Look up status, event, and reason enums | [Statuses and codes](/docs/reference/statuses-and-codes) |
| Handle failed or review-required results | [Failure and Review Events](./failure-payload) |
| Verify that a request came from Zyphe | [Webhook Signature](./webhook-signature) |
| Reconcile events with records in your system | [Custom Fields](./custom-fields) |
| Migrate from the legacy `data.kyc` format | [Backwards Compatibility](./backwards-compatibility) |
The pages above each own one part of the contract. Payload examples are kept in the payload reference rather than repeated throughout the integration guide.
---
## Webhook Payload
This page documents the objects carried by standard result webhooks and lifecycle notifications. The outer HTTP and envelope contract is defined once in [Receiving Webhooks](./webhook-structure).
## Standard result fields
Most result objects under `data` share these fields:
| Field | Meaning |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `version` | Payload schema version. |
| `id` | Identifier of the step result. It may contain a default value when processing failed before a result was created. |
| `verificationRequestId` | Verification request that produced the result. |
| `flowId` | Flow identifier. |
| `flowStepId` | Flow-step identifier. |
| `createdAt` | ISO 8601 timestamp associated with the request or result. |
| `customData` | Custom values attached to the verification request. See [Custom Fields](./custom-fields). |
| `status` | Status of the individual result. Do not confuse it with top-level `flowStatus`. |
| `identityEmail` | Email of the verified identity, when available. |
| `reason` / `reasons` | Structured failure or review context, depending on result type. |
| `errorMessage` | Optional processing diagnostic, when supported by the result type. |
The primary result key is the non-null value among `dv`, `poa`, `form`, `spid`, `phone`, `wallet`, `kyb`, `aml`, and `geolocation`. Other keys may be absent or `null`, depending on the serializer. Extended and legacy DV configurations can add companion values beside `dv`.
## Document Verification (`data.dv`)
```json
{
"version": 0,
"id": "d65fa9a0-596e-4d7e-afd1-59a23bfb5a74",
"verificationRequestId": "cf52e18e-28d1-4a2f-8304-c04ef5a75d0f",
"flowId": "2d8285d7-f4ba-42df-ab3f-681d9870d37a",
"flowStepId": "03559a0f-be8e-4ab7-964d-1ff4745d92a4",
"createdAt": "2025-04-25T19:32:14.558006634Z",
"customData": {},
"status": "PASSED",
"reasons": [],
"documentId": "d41bc0ea-3b5c-4342-a1ae-a46dd0a770f8",
"documentType": "Driving License",
"class": "web-mobile",
"platform": "iOS",
"browser": "Mozilla/5.0 ...",
"scoreBiometricSelfie": 99,
"errorMessage": null,
"processAdditionalData": {},
"identityEmail": "applicant01@example.com"
}
```
| DV-specific field | Meaning |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `reasons` | Structured reasons such as `DOCUMENT_EXPIRED`, `LOW_QUALITY`, or `UNDERAGE`. Empty when no reason is reported. |
| `documentId` | Identifier of the document stored for this result. |
| `documentType` | Human-readable document type. |
| `class`, `platform`, `browser` | Optional client context captured during verification. |
| `scoreBiometricSelfie` | Optional portrait-match percentage. |
| `processAdditionalData` | Additional processor output, when available. |
### Extended document verification (`data.additionalData`)
When Extended Webhook is enabled, `additionalData` is a sibling of `dv` inside `data`:
```json
{
"data": {
"dv": {
"id": "d65fa9a0-596e-4d7e-afd1-59a23bfb5a74",
"status": "PASSED"
},
"additionalData": {
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1990-01-15",
"documentNumber": "AB1234567",
"issuingState": "GBR",
"expiryDate": "2030-12-31",
"issuingDate": "2020-01-01",
"issuingAuthority": "DVLA",
"personalNumber": "",
"documentClassCode": "DL",
"selfie": {
"contentType": "image/jpeg",
"presignedUrl": "https://..."
}
}
}
}
```
`selfie.presignedUrl` is time-limited. Download it only when needed and apply the same access controls used for other identity data.
The legacy `data.kyc` companion is documented in [Backwards Compatibility](./backwards-compatibility).
## Proof of Address (`data.poa`)
In addition to the common result fields, proof-of-address results contain:
| Field | Meaning |
| -------------- | ----------------------------------------------------------------------- |
| `reason` | Optional failure reason. |
| `documentId` | Identifier of the uploaded address document. |
| `documentType` | Document type, such as `PAY_SLIP`, `UTILITY_BILL`, or `BANK_STATEMENT`. |
```json
{
"status": "PASSED",
"reason": null,
"documentId": "e04a00e6-ccf9-4593-ab86-4b2c01f3bb95",
"documentType": "UTILITY_BILL"
}
```
## Form (`data.form`)
| Field | Meaning |
| ---------- | -------------------------------------------------- |
| `reason` | Optional failure reason. |
| `sections` | Submitted answers grouped by form section. |
| `formId` | Identifier of the form definition, when available. |
| `formName` | Name of the form. |
```json
{
"status": "PASSED",
"reason": null,
"sections": [
{
"name": "Personal Information",
"items": [{ "name": "fullName", "value": "John Doe" }]
}
],
"formId": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"formName": "Customer Onboarding Form",
"errorMessage": null
}
```
## Phone Verification (`data.phone`)
Phone results include `reason`, `errorMessage`, and `phoneNumberLast4` in addition to the common fields.
```json
{
"status": "PASSED",
"reason": null,
"errorMessage": null,
"phoneNumberLast4": "1234"
}
```
:::info Phone-number privacy
The full phone number is not included in the webhook. Zyphe stores a SHA-256 hash and exposes only the final four digits in this result.
:::
## Wallet Verification (`data.wallet`)
| Field | Meaning |
| ------------------------ | ---------------------------------------------------------------------- |
| `message` | SIWX message signed by the user. |
| `signature` | Signature produced by the wallet. |
| `walletAddress` | Verified wallet address. |
| `walletChain` | Network used, such as `ETHEREUM`, `SOLANA`, or their testnet variants. |
| `reason`, `errorMessage` | Optional failure context. |
```json
{
"status": "PASSED",
"message": "example.com wants you to sign in with your Ethereum account...",
"signature": "0x1234567890abcdef...",
"walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"walletChain": "ETHEREUM",
"reason": null,
"errorMessage": null
}
```
## SPID / CIE (`data.spid`)
SPID/CIE results include optional `reason`, `errorMessage`, and `userInfo`. The `userInfo` object can contain:
- Name and identity attributes: `firstName`, `lastName`, `fullName`, `fiscalNumber`, `dateOfBirth`, `gender`
- Birth and address attributes: `placeOfBirth`, `countryOfBirth`, `address`
- Contact attributes: `email`, `mobilePhone`, `digitalAddress`
- Provider attributes: `id`, `username`, `spidCode`
- Business-account attributes: `companyName`, `registeredOffice`, `companyVatNumber`
Fields depend on the identity provider and may be `null`.
## Geolocation (`data.geolocation`)
```json
{
"status": "PASSED",
"reason": null,
"errorMessage": null,
"country": "IT",
"ipAddress": "203.0.113.10",
"deviceFingerprint": "device-fingerprint",
"vpn": false,
"blacklistedIp": false
}
```
| Field | Meaning |
| ------------------- | --------------------------------------------- |
| `country` | Resolved country, when available. |
| `ipAddress` | IP address evaluated for the result. |
| `deviceFingerprint` | Device fingerprint associated with the proof. |
| `vpn` | Whether VPN usage was detected. |
| `blacklistedIp` | Whether the IP matched a blacklist. |
## KYB (`data.kyb`)
KYB results are delivered using the standard envelope. They include the common result fields plus:
| Field | Meaning |
| ----------------------- | ------------------------------------------------------------- |
| `businessInformation` | Submitted and verified business information. |
| `documentUploads` | KYB document-upload state and metadata. |
| `uboDefinitions` | Ultimate Beneficial Owner definitions and verification state. |
| `directorDefinitions` | Director definitions and verification state. |
| `pepAndSanctionsStatus` | Consolidated PEP and sanctions state. |
| `nextAction` | Next action required by the KYB workflow, when any. |
| `assignedToIdentityId` | Reviewer assigned to the result, when any. |
| `fundedAt` | Funding timestamp, when applicable. |
| `report` | Generated report or report reference, when available. |
```json
{
"status": "REQUIRES_MANUAL_REVIEW",
"reason": null,
"errorMessage": null,
"businessInformation": {},
"documentUploads": [],
"uboDefinitions": [],
"directorDefinitions": [],
"pepAndSanctionsStatus": null,
"nextAction": null
}
```
## AML (`data.aml`)
AML deliveries represent produced results, scheduled refreshes, and moderation changes. Top-level `event` and inner `updateKind` use `PRODUCED`, `REFRESHED`, or `MODERATED`.
```json
{
"event": "PRODUCED",
"data": {
"aml": {
"version": 0,
"id": "e5f6a7b8-9012-3456-7890-abcdef123456",
"verificationRequestId": "cf52e18e-28d1-4a2f-8304-c04ef5a75d0f",
"flowId": "2d8285d7-f4ba-42df-ab3f-681d9870d37a",
"flowStepId": "03559a0f-be8e-4ab7-964d-1ff4745d92a4",
"createdAt": "2026-07-14T10:15:30Z",
"updateKind": "PRODUCED",
"status": "PENDING",
"subjectType": "PERSON",
"hasPep": false,
"hasSanctions": false,
"riskScorePercent": 12
}
}
}
```
| Field | Meaning |
| ------------------ | ---------------------------------------------------------------------- |
| `updateKind` | Why this webhook was emitted: `PRODUCED`, `REFRESHED`, or `MODERATED`. |
| `status` | Moderation state: `PENDING`, `APPROVED`, `REJECTED`, or `ESCALATED`. |
| `subjectType` | Screened subject type, such as `PERSON` or `COMPANY`. |
| `hasPep` | Whether PEP matches are present. |
| `hasSanctions` | Whether sanctions matches are present. |
| `riskScorePercent` | Optional risk score from 0 to 100. |
## Notification events
Notification webhooks use top-level `eventData`, not `data`, and omit `flowStatus`. See the full envelope in [Receiving Webhooks](./webhook-structure#notification-envelope).
### `DOCUMENT_EXPIRED`
```json
{
"version": 0,
"type": "DOCUMENT_EXPIRED",
"flowResultId": "936f35a8-4921-430e-b016-15be48968886",
"verificationRequestId": "cf52e18e-28d1-4a2f-8304-c04ef5a75d0f",
"flowId": "2d8285d7-f4ba-42df-ab3f-681d9870d37a",
"flowStepId": "03559a0f-be8e-4ab7-964d-1ff4745d92a4",
"expiryDate": "2025-04-25",
"customData": {},
"documentId": "d41bc0ea-3b5c-4342-a1ae-a46dd0a770f8",
"documentType": "Driving License",
"identityEmail": "applicant01@example.com"
}
```
### `KYC_RESULT_EXPIRED`
| Field | Meaning |
| --------------- | ------------------------------------------------ |
| `flowResultId` | Expired flow-result identifier. |
| `flowId` | Associated flow. |
| `identityId` | Identity associated with the expired KYC result. |
| `dvResultId` | Document-verification result that expired. |
| `identityEmail` | Identity email, when available. |
### `FLOW_RISK_REEVALUATED`
| Field | Meaning |
| ----------------------------- | ----------------------------------------------------------- |
| `flowResultId` | Re-evaluated flow result. |
| `scoreBefore`, `scoreAfter` | Previous and current composed scores. Either may be `null`. |
| `statusBefore`, `statusAfter` | Previous and current flow statuses. |
Use this event to refresh downstream decisions when AML moderation, scheduled AML refreshes, manual review, or administrative scoring changes the composed risk result.
---
## Webhook Signature
Zyphe signs webhook requests with HMAC-SHA256 when your organization has a webhook secret configured. Verify the signature before parsing or processing the payload.
## Signature format
The `X-Signature` header contains a Unix timestamp and a hexadecimal signature:
```text
t=,v0=
```
Zyphe constructs the signed value from the timestamp, a dot, and the exact JSON request body:
```text
signed_payload = "{timestamp}.{raw_json_payload}"
```
The organization webhook secret is a hexadecimal string. Decode it into bytes, then calculate the hexadecimal HMAC-SHA256 digest of `signed_payload`.
## Verification steps
1. Read `X-Signature` and extract `t` and `v0`.
2. Reject an invalid timestamp or one outside your accepted tolerance. A five-minute tolerance is a common starting point for replay protection.
3. Read the raw, unmodified request body. Do not parse and re-serialize it before verification.
4. Construct `{t}.{raw_request_body}`.
5. Decode your hexadecimal webhook secret and calculate the HMAC-SHA256 digest.
6. Compare the computed hexadecimal digest with `v0` using a constant-time comparison.
7. Parse and process the request only after all checks pass.
If the signature is missing, malformed, expired, or does not match, return an error and do not process the event. Keep the secret server-side and rotate it immediately if it is exposed.
---
## Receiving Webhooks
This page defines the HTTP delivery contract and the two top-level payload shapes. Step-specific fields are documented separately in [Webhook Payload](./webhook-payload).
## Delivery contract
| Property | Value |
| -------------------------- | ------------------------------------------------- |
| Method | `POST` |
| Content type | `application/json` |
| Signature header | `X-Signature` when a webhook secret is configured |
| Successful acknowledgement | `200 OK` |
| Delivery attempts | Up to four total attempts when delivery fails |
Your endpoint should:
1. Capture the unmodified request body.
2. Verify `X-Signature` before trusting the body.
3. Parse the JSON only after verification.
4. Store or enqueue the event using idempotent application logic.
5. Return `200 OK` promptly.
See [Webhook Signature](./webhook-signature) for the exact HMAC input and header format.
## Standard result envelope
Verification-result webhooks use this envelope:
```json
{
"resultId": "936f35a8-4921-430e-b016-15be48968886",
"event": "COMPLETED",
"data": {
"dv": {
"id": "d65fa9a0-596e-4d7e-afd1-59a23bfb5a74",
"status": "PASSED"
}
},
"custom": {},
"flowStatus": "COMPLETED"
}
```
| Field | Meaning |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `resultId` | Identifier of the overall flow result. |
| `event` | The event for this delivery. Common verification values include `COMPLETED`, `FAILED`, `REVIEW`, and `REJECTED`. AML updates use `PRODUCED`, `REFRESHED`, or `MODERATED`. |
| `data` | The typed result object. The primary key identifies the result type. |
| `custom` | Flow-level custom data used to reconcile the result with your system. |
| `flowStatus` | Current status of the overall flow, which may differ from the individual step status. |
The primary `data` keys are `dv`, `poa`, `form`, `spid`, `phone`, `wallet`, `kyb`, `aml`, and `geolocation`. Extended DV webhooks also include `additionalData`; organizations using legacy compatibility may also receive `kyc` beside `dv`.
## Notification envelope
Lifecycle notifications have a different shape. They use top-level `eventData` and intentionally omit `data` and `flowStatus`:
```json
{
"resultId": "936f35a8-4921-430e-b016-15be48968886",
"event": "NOTIFICATION",
"eventData": {
"version": 0,
"type": "DOCUMENT_EXPIRED",
"flowResultId": "936f35a8-4921-430e-b016-15be48968886"
},
"custom": {}
}
```
Route notifications by `eventData.type`. Current notification types are:
- `DOCUMENT_EXPIRED`
- `KYC_RESULT_EXPIRED`
- `FLOW_RISK_REEVALUATED`
Their fields are listed under [Notification Events](./webhook-payload#notification-events).
## Dispatching events
Use the envelope before inspecting step-specific fields:
```ts
if (payload.event === 'NOTIFICATION') {
handleNotification(payload.eventData.type, payload.eventData)
} else if (payload.data.aml) {
handleAmlUpdate(payload.event, payload.data.aml)
} else {
const resultTypes = ['dv', 'poa', 'form', 'spid', 'phone', 'wallet', 'kyb', 'geolocation']
const resultType = resultTypes.find((key) => payload.data[key] != null)
handleVerificationResult(resultType, payload)
}
```
Do not assume that `resultId` uniquely identifies one delivery: a flow can produce multiple step results and later updates. Choose an idempotency key that includes the result type and its result identifier or update kind.
---
## Document Selection
The Document Selection step allows users to choose the specific type of identity document they wish to use for verification. This step acts as a routing mechanism, ensuring that the subsequent verification steps are configured to handle the selected document type and issuing country.
## How It Works
This step is typically placed at the beginning of a verification flow. It presents the user with a filtered list of supported countries and document types based on the organization's configuration.
Once a user makes a selection, the flow stores this preference and uses it to initialize subsequent steps such as Document Verification (DV), SPID (Italian Digital Identity), or Proof of Address (PoA). This ensures a tailored experience where the user is only asked for information relevant to their specific document.
## Customize Settings
### General Details
- **Name:** The name of the flow step for identification within the dashboard.
- **Countries Filter:** Define a whitelist or blacklist of countries. Users will only be able to select documents from allowed countries.
- **Document Type Whitelist:** Specify which categories of documents are acceptable (e.g., Passport, National ID, Driver's License).
- **Allowed Documents Per Country:** A granular configuration that maps specific country codes to their respective allowed document types.
- **Government Providers:** Configuration for digital identity providers, such as SPID, mapping them to specific document types.
- **Success Button Label:** The text displayed on the button after the user has confirmed their selection.
## Related Documentation
- [Document Verification](./document-verification.md)
- [Proof of Address](./proof-of-address.md)
- [SPID / CIE](./spid-cie.md)
---
## Document Verification
Short product explainer: [How document verification works](/docs/how-it-works/document-verification).
## Customize Settings
### General Details
- **Name:** The name of the flow step. It will be used to identify the step in the flow.
- **Minimum Age:** Age threshold that will be considered valid for the verification flow.
- **Minimum Acceptable Document Score:** The system analyzes submitted documents and computes a score based on authenticity checks. This parameter sets the minimum score threshold required for document validation.
- **Success Button Label:** A custom label for the Continue button that is shown once the step is completed.
:::info
The Document Verification step extracts and validates information from identity documents. To add liveness verification (ensuring the person is real and matches the document), add a separate [Liveness Verification](./liveness.md) step after this one.
:::
### Country Filters
If not specified, by default all the countries are whitelisted.
- **Countries Whitelist:** Documents from countries **in** the list will be accepted.
- **Countries Blacklist:** Documents from countries **not in** the list will be accepted.
If both are configured, the whitelist will override the blacklist.
### Document Filters
- **Document Type Whitelist:** Only document types that are in the list will be accepted (eg. ID Card, Driving License, Passport). If not specified, all the document types are accepted.
---
## Form
## Create a New Form
In order to create a "Form" Flow Step, you first need to create a Form.
In the Zyphe Dashboard, navigate to the **Forms** section and click on the **Create** button.
:::info
A Form can be reused in multiple different Flow Steps.
:::
Create forms from the dashboard and preview the end-user experience as you edit.
You can also generate a first draft from a prompt, then customize the generated sections and fields in the form editor.
Use prompt generation to create a reusable form draft from a natural-language description.
### Section
A form is composed of one or more sections. Each section will be displayed to the user as a separate page.
Each section is composed of one or more items.
### Item
An item is one of the following types:
- **Input Field:** A form input field.
- **Name:** The "machine-friendly" name of the field. This will be used for the JSON entry key.
- **Label:** The user-facing label of the field.
- **Type:** Text, Textarea, Checkbox, Date, Select, File, Place
- **Required:** Whether the field is required or not.
- **Options (Select):** A list of predefined values for the user to choose from, with an optional "Other" free-text entry.
- **Options (File):** Accepted MIME types (e.g., `application/pdf`, `image/jpeg`) and the expected document type.
- **Separator:** A line that visually separates items within a section.
- **Plaintext:** Plain text displayed to the user in the form of one or more paragraphs.
Edit sections and form items directly in the form builder.
### Visibility Conditions
You can use visibility conditions to show or hide fields based on previous answers. This lets you keep forms shorter for users while collecting additional information only when it is relevant.
Configure conditions to control when specific fields are visible.Conditions can combine field values and operators to support dynamic form behavior.
### Scoring
Every input field has an optional **Score** panel in its settings, where you can assign score tags and factors based on the field's answer. The weights you configure here feed your organization's scores, by default the **Risk score**: which are computed on each flow result once the user completes the flow.
Expand the Score panel in a field's settings to score its answers.
The panel offers three mechanisms:
- **Option weights (Select fields):** Assign an optional score weight to each option; the weight is applied when that option is selected. Options with no weight simply don't contribute.
- **Score tags:** Attach one or more tags to the field, each with a **Weight**. A tag can be emitted unconditionally or **only when** the answer matches a condition, for example, emit a tag with weight `40` only when a checkbox is checked.
- **Contextual factors:** Attach multipliers that amplify the score instead of adding to it. A factor uses either a **fixed key** or, on Select fields, the **answer value**, and carries a **Multiplier**. Factors also support "only when" conditions.
Assign a score weight to each option of a Select field.Configure conditional score tags and contextual factors for a field.
If your organization has defined custom scores, each tag or factor also has a **Score** selector that chooses which score it feeds; otherwise everything feeds the default Risk score.
:::tip Scoring documentation
Score definitions, tag weights, and the math that turns them into a 0–100 score are covered in the dedicated [Scoring](/docs/guides/scoring) section, see in particular [Score Definitions](/docs/guides/scoring/score-definitions) and [How Scores Are Computed](/docs/guides/scoring/how-scores-are-computed).
:::
### Multilanguage Support
Forms support multiple editing languages, so you can localize section labels, field labels, and option labels for users in different locales.
Switch the editing language to localize form content.
## Customize Settings
- **Name:** The name of the flow step. It will be used to identify the step in the flow.
- **Form:** The form to be embedded in the flow step.
- **Success Button Label:** A custom label for the Continue button that is shown once the step is completed.
---
## Geolocation Verification
The Geolocation Verification step allows organizations to verify the physical location of a user during the verification flow. This process cross-references the device's reported coordinates with network-level data to ensure the user is present in an authorized region and is not attempting to mask their location.
## How It Works
The verification process follows a two-step sequence to ensure the integrity of the location data:
1. **Initialization:** The client requests a geolocation verification start. The system generates and returns a unique, time-limited nonce.
2. **Proof Submission:** The client submits the geolocation proof, which includes GPS coordinates, device fingerprint, the provided nonce, permission status, and network metadata such as RTC candidates.
3. **Validation:** The system performs reverse geocoding on the coordinates to determine the country. This is then compared against the IP-based country data derived from request headers or Cloudflare trace data.
4. **Result:** If the coordinates and IP country do not match, the verification fails with a "location_ip_mismatch" reason.
## Customize Settings
### General Details
- **Name:** The name of the flow step used for internal identification.
- **VPN Detection:** When enabled, the system checks if the user is connecting through a known VPN or proxy service.
- **IP Validation:** Enables strict validation of the user's IP address against known blacklists and reputation databases.
- **Location Cross-Check:** Performs a mandatory comparison between the device's GPS coordinates and the network's reported location.
- **Success Button Label:** The text displayed on the button after a successful location verification.
## Technical Details
- **Rate Limiting:** The initialization endpoint is limited to 10 requests per 60 seconds. The verification submission endpoint is limited to 10 requests per 120 seconds.
- **Nonce Expiry:** The verification nonce is valid for 300 seconds (5 minutes). Proofs must be submitted within this window.
- **Result Fields:** The verification result includes the status, failure reason (if applicable), detected country, IP address, device fingerprint, VPN status, and whether the IP is blacklisted.
## Related Documentation
- [Creating a Flow](./index.mdx)
- [Document Verification](./document-verification.md)
- [Liveness Verification](./liveness.md)
---
## KYC Overview
KYC verifies the identity of an **individual**. A KYC flow can combine identity-document checks, liveness detection, proof of address, phone verification, forms, and other steps to collect the evidence your organization needs for onboarding and compliance.
This guide explains how to create and configure a KYC flow in Zyphe, from selecting the flow type to testing SDK and webhook integrations.
## Create a KYC Flow
### 1. Log in to the admin panel
Login to the Zyphe Dashboard and navigate to the **Flows** section.
### 2. Click the "Create Flow" button
Click on the **Create Flow** button on the top right.
Open the Flows page and click Create Flow to start a new flow.
Zyphe supports two types of flows:
- **KYC:** Use this flow type to verify individuals.
- **KYB:** Use this flow type to verify businesses.
For this tutorial, we will create a KYC flow.
Choose whether to create a KYC flow for individuals or a KYB flow for businesses.
### 3. Configure the flow
Customize the flow settings:
- **Name:** the name of the flow.
- **Slug:** a short identifier for the flow. It will be used to identify the flow within the link that is going to be shared with the end users that goes through the verification process.
- **Success URL:** the link where we redirect the users once the verification process is completed.
- **Webhook URL:** the URL of the webhook to be called when relevant events (such as verification completion or failure) happens on the current flow.
Enter the flow name, slug, success URL, and optional webhook URL.
### 4. Optional: Invite partner organizations
If you want to share verification results with partner organizations, you can enable **KYC Passport** for this flow. This allows you to create a trusted ecosystem where verified identity data is securely shared with authorized partners.
KYC Passport enables you to invite partner organizations to access KYC verification results from your flow. When users complete verification, they provide consent for their data to be shared with all approved partners, eliminating the need for repeated verification across your ecosystem.
:::tip[Step-by-step guide]
For a detailed walkthrough with screenshots on how to invite partner organizations, see the [KYC Passport: Inviting Partner Organizations](/docs/guides/kyc-passport) guide.
:::
For technical details about how KYC Passport works, including security considerations and use cases, see the [KYC Passport technical documentation](/docs/data-flows/kyc-passport).
### 5. Configure the flow settings
Use the flow configuration panel to update the flow endpoints and advanced settings after the flow has been created.
- **Success URL:** the page where users are redirected after they complete all flow steps.
- **Webhook URL:** the endpoint that receives webhook events for the flow.
- **Webhook secret:** the secret used to verify webhooks sent by the platform.
Use the flow settings action to review or update endpoint settings after creation.Configure redirect and webhook endpoints, and copy or rotate the webhook secret.
### 6. Create a flow step
Click **Add Step** to start creating the verification steps for the current flow. A common KYC setup starts with **Document Selection** and then adds **Document Verification**.
- **Document Verification:** Scans and validates identity documents using the phone camera. Extracts information from documents and performs authenticity checks. Supports documents from 190+ countries including ID cards, passports, and driver's licenses.
- **Liveness Verification:** Performs active liveness detection to ensure the person is real and matches the portrait in their identity document. Must be linked to a Document Verification step.
- In order to add a Liveness Verification step, you need to first add a Document Verification step.
- **Proof of Address:** Allows the users to submit documents that attest their residency such as utility bills, pay slip, financial documents and others.
- In order to add a Proof of Address step, you need to first add a Document Verification step or a SPID step.
- **Phone Verification:** Allows users to verify their phone number through an SMS-based OTP (One-Time Password) authentication flow.
- Please contact support to enable this step type.
- **Form:** Allows to collect additional information. The form step allows to embed any form that has been created with the Form builder.
- In order to add a form step, you first need to create a Form with the Form builder.
- **Wallet Verification:** Allows users to prove ownership of their cryptocurrency wallet by connecting their wallet and signing a cryptographic message. Supports Ethereum and Solana networks.
- **SPID:** Allows the users to submit their SPID (Italian digital identity card) to verify their identity.
- Please contact support to enable this step type.
- **Document Selection:** Allows users to choose which type of identity document they want to use for verification. Acts as a routing step before Document Verification, SPID, or Proof of Address steps.
- **Geolocation Verification:** Verifies the physical location of a user by cross-referencing device GPS coordinates with network-level IP data.
- **Sharing:** Enables the secure transfer of previously verified identity data to partner organizations, supporting reusable credentials and one-click KYC flows.
Let's start by creating a new Document Verification step
Click Add Step from the new flow page to begin adding verification steps.Select the type of flow step you want to add.Start with Document Selection so users can choose their document type.The Document Selection step appears in the flow after it is added.
After that we can proceed with adding the Document Verification step
Open the Add Flow Step dialog again to add Document Verification.Select Document Verification and link it to the Document Selection step.The flow now contains both Document Selection and Document Verification steps.
### 7. SDK snippets
Use the SDK snippets panel to generate implementation examples for the selected flow and step. These snippets help you start an SDK integration with the correct identifiers and environment values.
Open SDK snippets from the step devtools panel.Copy a generated SDK snippet with the flow and step identifiers already filled in.
### 8. Webhook trigger
Use the webhook trigger tool to manually send a webhook event for a flow step. This is useful when testing your webhook endpoint before sending users through the flow.
Open the webhook trigger tool from the step devtools panel.Trigger a test webhook event for a selected flow step and status.
### 9. Webhook monitor
Use the webhook monitor to inspect webhook delivery attempts, payloads, responses, and errors while testing your integration.
Open the webhook monitor from the step devtools panel.Inspect captured webhook events, delivery attempts, payloads, and errors.
### 10. Flow Builder
Flow Builder is an alternative way to build flows. Use it when you need advanced patterns such as if conditions, custom logic, branching paths, and more complex step orchestration.
Open Flow Builder from the flow page when you need advanced orchestration.Use the Flow Builder canvas to arrange steps and connect branching paths.Add new nodes to define additional actions, checks, or branches.Configure switch conditions to route users through different flow paths.
#### Conditions with custom data and vault fields
Flow Builder conditions can use custom data passed into the verification flow. This is useful when you want to route users based on information from your own system, such as customer segment, onboarding type, product plan, or other context attached to the verification session.
Conditions can also use selected fields extracted from vault documents, such as age, nationality, document metadata, or address-related values. These fields are handled with privacy in mind: Zyphe does not store the document details unencrypted just to make workflow decisions. Instead, the relevant condition is evaluated securely when the verified document data is available, and the workflow keeps only the outcome needed to continue routing the flow.
---
## Liveness Verification
The Liveness Verification step ensures that the person completing the verification is a real human and matches the portrait in their identity document. This step must be linked to a Document Verification step.
Short product explainer: [How liveness works](/docs/how-it-works/liveness).
## Overview
The liveness check performs active liveness detection using facial recognition technology to:
1. Verify that a live person is present (not a photo, video, or mask)
2. Match the live portrait against the portrait extracted from the identity document
3. Optionally perform biometric uniqueness checks to prevent duplicate identities (ask the support to enable the functionality in case of need)
## How It Works
1. **Prerequisites**: A Document Verification step must be completed first to extract the portrait image from the identity document
2. **User Flow**: The user is prompted to position their face in the camera frame and follow on-screen instructions
3. **Active Liveness**: The system performs active liveness detection to ensure a real person is present
4. **Face Matching**: The live portrait is compared against the portrait from the document
5. **Validation**: The system validates that the match score meets the configured threshold
## Customize Settings
### General Details
- **Name:** The name of the flow step. It will be used to identify the step in the flow.
- **Document Step:** The Document Verification step that this liveness check is linked to. The liveness check will use the portrait extracted from the document in this step for comparison.
- **Success Button Label:** A custom label for the Continue button that is shown once the step is completed.
### Thresholds
- **Minimum Portrait Match Percent:** The minimum similarity score (0-100%) required between the live portrait and the document portrait. Default: 75%
- Higher values increase security but may result in more false rejections
- Lower values are more permissive but may reduce security
- **Biometric Match Percent:** The minimum similarity score required for biometric uniqueness checks when biometric checking is enabled. Default: 75% (ask the support if you need to change the value)
## Best Practices
### For End Users
The liveness check works best when users:
- **Use good lighting**: Ensure the face is well-lit without harsh shadows
- **Find a neutral environment**: Use a plain background without distractions
- **Position properly**: Keep the face centered in the frame
- **Remove accessories**: Take off glasses, hats, or masks that might obscure the face
## Common Issues
### Low Match Score
If users frequently fail the liveness check:
- Check if the portrait in the document is clear and recent
- Verify that lighting conditions are adequate
- Ensure users are following the on-screen instructions
## Technical Details
The liveness verification step:
- Uses active liveness detection (user interaction required)
- Performs face matching using advanced biometric algorithms
- Stores the live portrait securely in the user's vault
- Can trigger biometric uniqueness checks across on a per-flow basis
## Related Documentation
- [Document Verification](./document-verification.md) - Must be configured before adding a liveness step
- [Webhook Payload](/docs/guides/webhook/webhook-payload.md) - Learn about the webhook events for liveness verification
---
## Phone Verification
The Phone Verification step allows you to verify users' phone numbers through an SMS-based OTP (One-Time Password) authentication flow.
## How It Works
1. **Phone Number Entry:** The user enters their phone number in international format (E.164).
2. **OTP Delivery:** The system sends a 6-digit verification code via SMS to the provided phone number.
3. **Code Verification:** The user enters the received code to complete the verification.
4. **Completion:** Once verified, the phone number is securely stored (hashed) and the verification step is marked as complete.
## Customize Settings
### General Details
- **Name:** The name of the flow step. It will be used to identify the step in the flow.
- **Success Button Label:** A custom label for the Continue button that is shown once the step is completed.
## Technical Details
### Phone Number Format
Phone numbers must be provided in **E.164 format**, which includes:
- A leading `+` sign
- Country code (1-3 digits)
- Phone number (up to 15 digits total)
**Examples:**
- `+1234567890` (Valid)
- `+19876543210` (Valid)
- `1234567890` (Invalid - missing `+`)
### Security Features
- **OTP Expiration:** Verification codes expire after 3 minutes (180 seconds).
- **Rate Limiting:**
- Starting verification: Maximum 3 requests per 5 minutes per verification request.
- Code verification: Maximum 5 attempts per 3 minutes per verification request.
- **Data Privacy:** Phone numbers are stored as SHA-256 hashes. Only the last 4 digits are stored in plain text for reference.
### SMS Delivery
SMS messages are sent via AWS SNS with the sender ID "Zyphe" and include the verification code in the user's locale.
## Requirements
:::info
The Phone Verification product must be enabled for your organization. Please contact support if you need access to this feature.
:::
---
## Proof of Address
Short product explainer: [How proof of address works](/docs/how-it-works/proof-of-address).
## Customize Settings
- **Name:** The name of the flow step. It will be used to identify the step in the flow.
- **Document Types:** The types of documents to be accepted as valid proof of residency.
- **Maximum Document Age (in days):** If the submitted document is older than the specified threshold, it will be rejected.
- **Success Button Label:** A custom label for the Continue button that is shown once the step is completed.
---
## Sharing
The Sharing step enables the secure transfer of verified identity data between different organizations or flows. This feature supports the concept of reusable credentials, allowing users who have already completed a verification process to share their existing data with partners without undergoing the full verification process again.
## How It Works
When a user reaches a Sharing step in a flow, the system checks for existing verified data associated with that user's identity (often referred to as a KYC Passport).
If valid data is found, the Sharing step facilitates the automatic population of the target flow with the verified attributes. The organization defines which partner flows are authorized to receive this data and which specific verification components (such as Document Verification or Liveness) should be shared. This significantly reduces friction for the user while maintaining high security standards.
## Customize Settings
### General Details
- **Name:** The internal identifier for this flow step.
- **Partner Configurations:** A list of authorized partner flows. Each configuration specifies the unique ID of the partner flow and the types of verification data (e.g., Document Verification, Form data, Liveness) that are permitted to be shared.
- **Success Button Label:** The text displayed on the button after the user consents to share their data.
## Related Documentation
- [Reusable Credentials](/docs/reusable-credentials-one-click-kyc)
- [KYC Passport](/docs/data-flows/kyc-passport)
- [Creating a Flow](./index.mdx)
---
## SPID / CIE
The Zyphe platform supports the Italian Identity providers SPID and CIE (Electronic ID Card) as an identity verification method.
The SPID/CIE verification can be added as a dedicated step in verification flow.
## How it works
For more details check the [SPID/CIE integrations section](/integrations/italian-digital-identity-ecosystem-spid-and-cie.md).
---
## Wallet Verification
The Wallet Verification step allows users to prove ownership of their cryptocurrency wallet addresses by connecting their wallet and signing a cryptographic message. This provides a secure and decentralized way to verify wallet ownership without exposing private keys.
## Supported Blockchains
The wallet verification step supports the following blockchain networks:
- **Ethereum** (Mainnet)
- **Solana** (Mainnet)
- **Ethereum Testnet** (Sepolia)
- **Solana Testnet** (Devnet)
## How It Works
The wallet verification process follows these steps:
1. **Wallet Connection**: The user connects their crypto wallet using a compatible wallet provider (e.g., MetaMask, Phantom, WalletConnect).
2. **Message Generation**: The system generates a unique cryptographic message that includes:
- Domain information
- Verification request details
- Timestamp and expiration time (5 minutes)
- Unique nonce for security
3. **Message Signing**: The user signs the generated message with their wallet's private key.
4. **Signature Verification**: The system verifies the signature cryptographically to confirm wallet ownership.
5. **Result Storage**: Upon successful verification, the wallet address, chain, signed message, and signature are securely stored.
## Customize Settings
### General Details
- **Name:** The name of the flow step. It will be used to identify the step in the flow.
- **Chains:** Select one or more blockchain networks that users can use for wallet verification. You can allow multiple chains to give users flexibility in choosing their preferred network.
- **Success Button Label:** A custom label for the Continue button that is shown once the step is completed.
### Security Features
The wallet verification implements several security measures:
- **Message Expiration:** Generated messages expire after 5 minutes to prevent replay attacks.
- **Rate Limiting:** Signature verification is rate-limited to 5 attempts per minute per verification request.
- **Domain Verification:** The system validates that the message domain matches the expected domain.
- **URI Validation:** The verification ensures the message URI corresponds to the correct verification request.
- **Nonce Generation:** Each message includes a unique nonce to prevent message reuse.
## Technical Details
The wallet verification step uses the **SIWX (Sign-In With X)** standard, which provides a blockchain-agnostic approach to wallet-based authentication. The implementation supports:
- **Ethereum chains:** Uses SECP256K1 signature verification
- **Solana chains:** Uses ED25519 signature verification
## Use Cases
Wallet verification is particularly useful for:
- **DeFi Platforms:** Verify user wallet ownership for decentralized finance applications
- **NFT Marketplaces:** Confirm wallet ownership before allowing trading or minting
- **Token Gating:** Verify wallet addresses to gate access to exclusive content or services
- **Compliance:** Add wallet verification as part of a comprehensive KYC flow
- **Airdrop Eligibility:** Verify wallet ownership for token distribution
## Configuration Example
Example of wallet verification step configuration
## Webhook Payload
When a wallet verification is completed, a webhook is triggered (if configured in the flow settings) with the following information:
- Verification request details
- Wallet address
- Blockchain network used
- Signed message
- Signature
- Verification timestamp
## Best Practices
1. **Multiple Chain Support:** Enable multiple blockchain networks to accommodate users with different wallet preferences.
2. **Clear Instructions:** Provide clear guidance to users about which wallets are supported for each chain.
3. **Error Handling:** Inform users if their signature verification fails and allow them to retry.
4. **Combine with Other Steps:** Consider combining wallet verification with document verification for enhanced KYC/KYB processes.
## Notes
- The verification message must be signed within 5 minutes of generation.
- Each wallet address can be verified once per verification request.
- The signed message and signature are stored for audit purposes.
- Testnet chains should only be used for development and testing purposes.
---
## Billing
Use the Billing page to activate a plan, manage billing details, set trial reminders, and monitor credit usage.
## Open Billing
You can open billing in two ways:
- Click **Open billing** from the trial banner on the dashboard.
- Open **Settings** and switch to the **Billing** tab.
Open billing from the trial banner or from Settings.
## Update Your Plan
In the **Billing** tab, the **Current Plan** card shows your active plan or trial state. Click **Update** or **Manage Billing** to change your subscription. The plan dialog shows the plans currently available for your organization.
Use the Billing tab to update your plan and review current plan status.
## Complete Checkout
After choosing a plan, complete checkout through the Stripe-hosted checkout page. Select the currency, enter the billing email, and start the trial or subscription.
Complete checkout in Stripe to activate the selected plan.
## Billing Actions
The Billing page also includes actions for managing billing information and reminders.
- **Manage Billing:** opens the billing portal for subscription and payment management.
- **Billing Contact:** lets you customize the billing contact information shown for your organization.
- **Trial reminder:** sends a reminder before the trial expires.
Use billing actions to manage billing details, update the billing contact, and send trial reminders.
## Usage and Credits
The **Usage This Period** section shows how credits are consumed during the current billing period. It breaks usage down by product, event count, credit cost, credits consumed, and overage.
The top bar also shows the remaining credit balance, so you can monitor available credits while navigating the dashboard.
Track product usage, consumed credits, overage, and remaining credits from the Billing page.
---
## Dashboard
### Overview
The Zyphe Dashboard is a business-to-business (B2B) plug-and-play solution that facilitates the verification of customer identity efficiently and accurately. Designed to offer a comprehensive suite of tools for conducting and managing KYC and KYB processes.
### Features
- **Verification Flows Overview**: Provides a quick glance at all verification requests that have been performed along with the status for each step of the verification flow.
- **Verification filters**: It is possible to filter verification attempts by email and/or any custom parameters that has been passed during the verification session.
- **Document Verification**: The system supports verification of official documents, including ID cards, driver's licenses and passports with details such as document type and creation dates.
- **Biometric Analysis**: Incorporates biometric verification scores, including facial recognition and life proof scores, to ensure the authenticity of the individual.
- **Result Management**: Presents the verification status (e.g., "success") and allows for viewing detailed results, enhancing decision-making processes.
### Security & Compliance
Our KYC Dashboard adheres to stringent security protocols, ensuring that all data is encrypted and handled in full compliance with GDPR and other relevant privacy laws. Regular audits and updates ensure that the dashboard remains compliant with the latest regulatory requirements.
### Access the Dashboard
Sign up for an account on the Zyphe Dashboard.
-- OR --
Reach out to our support team to obtain your credentials. With your credentials in hand, you can log in to the dashboard.
---
## Manage Users
Use the Users and Invites pages to add teammates to your organization, assign roles, and manage flow access for partner users.
## Invite a New User
Open the **Invites** page from the sidebar to invite users to your organization.
Use the Invites page to send new organization invitations.
Click **Invite Users** to open the invitation dialog.
The Invites page lists pending invitations and provides the Invite Users action.
Add one or more email addresses, then assign a role to each invitee.
Add email addresses before selecting roles and sending invitations.
Review the permission summary before sending the invitations. The summary shows the effective permissions for each selected role.
Use the role permission summary to confirm what each invited user can access.
Pending invitations can be resent or deleted from the action menu.
Use invite actions to resend a pending invite or delete it.
## Role Permissions
Zyphe roles are permission bundles. The main organization roles are:
| Role | Description | Main permissions |
| --------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Admin** | Full access to all organization features | Manage flows, flow results, flow access, users, API keys, bots, risk tags, partner invites, forms, organization settings, billing, webhooks, AML cases, and business verification. |
| **Operator** | Read-only access to organization data | Manual review, flow result management, advanced logs, and AML case management. |
| **AML Officer** | Access to AML monitoring and case management | Flow results, AML case management, and escalated AML cases. |
| **Developer** | Access to organization development features | Manage flows, API keys, bots, webhook triggers, and webhook secrets. |
| **Partner** | Access to shared flows only | Manage flow results for flows explicitly shared with the partner user. |
:::info Sandbox permissions
In sandbox, Admin and Operator users can also delete flow steps. This is intentionally limited to sandbox workflows.
:::
## Manage Partner Flow Access
Open the **Users** page to review organization users. If a user has the **Partner** role, you can manage which flows they can access.
Partner users expose a Manage Flow Access action from the Users table.
Flow access is granted per flow. You can grant access to specific flows and manage whether the partner has read-only or write access.
Grant partner users access to specific flows and enable write access when needed.
Partner flow permissions work as follows:
- **Read access:** lets the partner user view and manage results for the shared flow.
- **Write access:** lets the partner user write to the shared flow when write access is enabled and has not been revoked.
- **Revoked write access:** removes write permissions while preserving the existing shared-flow relationship.
---
## Passkey Authentication
Zyphe provides full FIDO2/WebAuthn support for passwordless authentication, allowing users to register and authenticate using biometric sensors, security keys, or mobile devices.
## How It Works
### Registration Flow
1. **Initiation:** The client calls the registration start endpoint to receive a cryptographic challenge. This requires an active JWT session.
2. **Credential Creation:** The user's device generates a new passkey and signs the challenge.
3. **Completion:** The client submits the signed credential to the registration endpoint. The system verifies the signature and stores the public key.
### Authentication Flow
1. **Initiation:** The client requests an authentication challenge by providing either a specific credential ID or the user's email.
2. **Assertion:** The user's device signs the challenge using the previously registered passkey.
3. **Completion:** The client submits the authentication response. Upon successful verification, the system returns a session token or verification request ID.
## API Endpoints
| Method | Path | Description |
| :----- | :--------------------------------- | :---------------------------------------------------------------------------------------------------------- |
| GET | `/auth/register-passkey/start` | Initiates passkey registration. Returns a challenge. Requires JWT authentication. |
| POST | `/auth/register-passkey` | Completes registration with the signed credential. Accepts `registrationResponse` and optional `prfSalt`. |
| POST | `/auth/authenticate-passkey/start` | Initiates authentication. Accepts optional `credentialId` or `email`. Returns challenge and options. |
| POST | `/auth/authenticate-passkey` | Completes authentication. Accepts `authenticationResponse` and `tokenType` (Bearer or VerificationRequest). |
## Security Features
- **Challenge Expiry:** Registration and authentication states are cached for 300 seconds (5 minutes). Challenges must be completed within this window.
- **PRF Support:** Supports the Pseudo Random Function (PRF) extension for WebAuthn, enabling client-side vault encryption.
- **Vault Integration:** Upon registration, the system automatically creates or unlocks the user's secure vault using a passkey-derived key.
- **Token Types:** Supports `Bearer` tokens for dashboard access (invalidates cache) and `VerificationRequest` tokens for identity verification flows (retains cache for vault access).
## Related Documentation
- [Dashboard](/docs/guides/dashboard)
- [SDK Integration](/docs/guides/sdk)
---
## Theming
You can match the verification flow to your brand. There are two layers, and they compose:
1. **Dashboard themes**: configured per organization in the Zyphe dashboard and saved. A theme defines light and dark color sets, a shared border radius, an optional custom font, and a logo. Each flow either selects a theme or uses the organization default.
2. **SDK overrides**: colors and border radius passed when you build a verification session URL. These override the configured theme **for that session only**, without changing anything saved in the dashboard.
Use the dashboard for your standing brand, and SDK overrides when a single integration needs different colors (for example, embedding the flow inside a host app that already has its own palette).
## Managing themes in the dashboard
Themes live in the dashboard under **Settings → Themes**. From the organization sidebar, open **Settings**, then select the **Themes** tab (you need the _Manage organization_ permission to see it). This page lists your themes and lets you create, edit, delete, and set a default.
### Sandbox and production are separate
Themes are scoped to the environment you are in: your **sandbox** themes and your **production** themes are two independent lists. Switching the dashboard between sandbox and production shows a different set of themes, and editing a theme in one environment never affects the other.
To avoid rebuilding a theme twice, design it in one environment and then copy it across. On any theme row, use the copy action, labelled **Copy to production** when you are in sandbox (and **Copy to sandbox** when you are in production). This duplicates the theme into the other environment as a new theme (`{name} (copy)`), which you can then rename or set as the default there.
### Default theme and per-flow overrides
Every organization has one **default** theme, marked _Default_ in the list. Any flow that hasn't been given a specific theme renders with the default. To change which theme is the default, open the theme's actions menu and choose **Set as default**.
To override the theme for a single flow:
1. Go to the **Flows** page and open the flow's **Flow configuration** dialog.
2. In the **Theme** dropdown, pick the theme this flow should use.
3. **Save**.
A flow keeps inheriting the organization default until you explicitly pick a theme here; once set, that flow always renders with the chosen theme (in the matching environment). Because theme selection is per environment, a flow resolves its theme from the sandbox or production theme list depending on where the session runs.
## Theme tokens
A theme is built from a small set of tokens, each available for **light** and **dark** mode:
| Token | What it controls |
| ------------ | --------------------------------------------------------------- |
| `background` | The page background behind the flow card |
| `accent` | The primary action color, buttons, icons, links, and highlights |
| `card` | The surface the flow card is drawn on |
Plus mode-agnostic tokens:
| Token | What it controls |
| -------------- | ------------------------------------------------------------ |
| `borderRadius` | Shared corner radius applied to cards, buttons, and inputs |
| `font` | A self-hosted custom font (`.woff2`), **dashboard only** |
| `logo` | A per-mode logo shown in the flow header, **dashboard only** |
Text colors are derived automatically for readability: the label color on an `accent` button and the text on a `card` are computed to contrast with their background, so you only set the surface colors.
> The custom **font** and **logo** can only be set in the dashboard, they are not overridable through the SDK.
## SDK overrides
Pass any of these on the `opts` object of `constructVerificationSessionUrl` (and `initializeZypheFlow` in the browser SDK). All are optional; anything you omit falls back to the flow's configured theme.
| Option | Maps to | Description |
| --------------------- | ------------------ | --------------------------------------- |
| `backgroundColor` | light `background` | Page background (light mode) |
| `accentColor` | light `accent` | Buttons, icons, highlights (light mode) |
| `cardColor` | light `card` | Card surface (light mode) |
| `darkBackgroundColor` | dark `background` | Page background (dark mode) |
| `darkAccentColor` | dark `accent` | Buttons, icons, highlights (dark mode) |
| `darkCardColor` | dark `card` | Card surface (dark mode) |
| `borderRadius` | `borderRadius` | Shared radius, e.g. `"12px"` |
Colors accept any CSS color value; we recommend hex codes. The dark-mode values apply when the flow renders in dark mode.
### Example
```ts
const url = constructVerificationSessionUrl({
flowParams: {
flowId: '...',
email: 'user@example.com',
isSandbox: false,
},
verificationSession: response.data, // from createVerificationSession
opts: {
apiKey: '',
environment: 'production',
// Light mode
backgroundColor: '#ffffff',
accentColor: '#155dfb',
cardColor: '#ffffff',
// Dark mode
darkBackgroundColor: '#16181d',
darkAccentColor: '#4f8bff',
darkCardColor: '#1f2229',
// Shared
borderRadius: '12px',
},
})
```
## Deprecated options
Earlier SDK versions exposed `primaryColor` and `secondaryColor`. They still work for backwards compatibility but are deprecated, prefer the named options above:
| Deprecated | Use instead | Notes |
| ---------------- | ----------------- | -------------------------------------- |
| `primaryColor` | `backgroundColor` | Historically controlled the background |
| `secondaryColor` | `accentColor` | Historically controlled the accent |
If both a deprecated option and its replacement are provided, the named option wins.
## Building the URL manually
If you are not using the SDK, append the equivalent query parameters to the flow URL yourself. Each SDK option maps to a `zyphe`-prefixed parameter:
| SDK option | URL parameter |
| ------------------------------- | -------------------------- |
| `backgroundColor` | `zypheBackgroundColor` |
| `accentColor` | `zypheAccentColor` |
| `cardColor` | `zypheCardColor` |
| `darkBackgroundColor` | `zypheDarkBackgroundColor` |
| `darkAccentColor` | `zypheDarkAccentColor` |
| `darkCardColor` | `zypheDarkCardColor` |
| `borderRadius` | `zypheBorderRadius` |
| `primaryColor` _(deprecated)_ | `zyphePrimaryColor` |
| `secondaryColor` _(deprecated)_ | `zypheSecondaryColor` |
Color values must be URL-encoded (a `#` becomes `%23`).
## Forcing light or dark mode
The color overrides above define _what_ the light and dark palettes look like. A separate parameter controls _which_ mode the flow renders in.
Append `zypheTheme` to the flow URL to force the color mode for that session:
| Value | Effect |
| -------- | ----------------------------------------------------------- |
| `light` | Always render the flow in light mode |
| `dark` | Always render the flow in dark mode |
| `system` | Follow the end user's operating-system / browser preference |
For example, `…&zypheTheme=dark` pins the session to dark mode regardless of the user's system setting. When `zypheTheme` is omitted, the flow follows the system preference. This parameter is independent of the color overrides, you can combine it with them (for instance, force `dark` and supply your `darkAccentColor`).
## Accessibility
The `accent` color is rendered as a solid element (button fills) on top of `card`, so it should stay visually distinct from the card surface, the dashboard theme editor warns when the contrast falls below the WCAG 1.4.11 non-text threshold (3:1). When supplying colors through the SDK, pick an `accent` that stands out against your `card`, and a `card`/`background` that the auto-derived text color can sit on comfortably.
---
## KYB Agentic Review Engine
The KYB agent that moderates business-information results runs on a **multi-role agentic review engine**. Rather than a single AI loop that both reasons and acts, the engine separates the work into distinct phases: three analytic roles assess the case, a deterministic (non-AI) guard decides whether the result may be approved, and a tool-constrained actuator carries out exactly one outcome. Every decision is recorded against a versioned policy and written to the audit trail.
This page describes how the engine works and how the [KYB Agent Mode](./index.mdx#kyb-agent-mode) you pick on the flow step controls it.
:::info
You do not configure the review engine directly. It is driven by the **KYB Agent Mode** on the KYB step (`Disabled`, `Suggest`, or `Auto-pilot`). This page describes what happens behind that setting.
:::
## How a review runs
When a KYB result is ready for moderation, the engine builds a **case file** from the `KybResult` and any open clarifications, then runs it through the pipeline below. Each phase has a single responsibility, and the analytic phase cannot mutate any record.
```mermaid
flowchart TD
A["KYB resultCase file: KybResult + open clarifications"] --> B["Prosecutorrisk findings: LLM, structured output"]
B --> C["Defendermitigations: LLM, skipped when zero findings"]
C --> D["Judgeverdict + rationale: LLM"]
D --> E["Deterministic guardhard veto: Approve + Deny ⇒ Escalate(Rust, no LLM)"]
E --> F["ActuatorONE permitted action: allowlist narrowed toJudge's decision ∩ Agent Mode gate"]
F --> G["KYB_AGENT_REVIEWED auditfindings count · verdict · guard outcome ·enacted decision · policy version · actuator status"]
```
### The three analytic roles
The analytic phase runs three roles in sequence: one raises concerns, one answers them, and a third weighs the two. None of them can change the stored record.
| Role | Responsibility | Notes |
| -------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| **Prosecutor** | Produces structured **risk findings** across the case file. | LLM with structured (schema-constrained) output. |
| **Defender** | Produces **mitigations** that answer the findings. | LLM. **Skipped** when the Prosecutor returns zero findings. |
| **Judge** | Weighs findings against mitigations and issues a **verdict with a rationale**. | LLM. Its decision is one of `Approve`, `Escalate`, or `Deny`. |
Each role uses a provider-agnostic, strict-mode structured-output schema and is driven by a versioned prompt (currently `kyb-policy-2026.06.0`).
### The deterministic guard
The Judge's verdict does not decide approval on its own. It is checked by a deterministic guard, written in Rust and running no LLM, that has the final say on whether a result may be approved. Approval requires all of:
- **PEP screening succeeded.**
- **Every document-backed field cross-check matched.**
- **No unresolved clarifications remain.**
If any condition fails, the guard vetoes the approval. An `Approve` verdict that coincides with a `Deny` signal is forced to **Escalate**. An LLM verdict cannot override the guard.
### The actuator
The actuator runs only after the guard, and it can take **only one action**. Its tool allowlist is the **intersection of the Judge's decision and the Agent Mode gate**:
- For an `Escalate` verdict, the `approve_kyb_result` tool is not in the allowlist and cannot be called.
- An `Approve` verdict under **Suggest** mode is recorded but not enacted.
- The mutating tools are idempotent, so a retry re-runs the same stateless evaluation instead of producing a second, independent action.
## How Agent Mode controls the engine
The [KYB Agent Mode](./index.mdx#kyb-agent-mode) on the flow step determines how far the actuator is allowed to go:
| Mode | Engine behavior |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Disabled** | The pipeline does not run. Fields still receive a match type, but there is no AI reasoning, verdict, or suggested value, operators moderate everything manually. |
| **Suggest** | The full pipeline runs and its verdict, rationale, and suggested values are surfaced to operators, but nothing is enacted. A human confirms or rejects. |
| **Auto-pilot** | The actuator may enact the decision automatically. An `Approve` verdict is enacted only when the deterministic guard also passes; anything the guard vetoes is escalated to the human review queue. |
## Guarantees
The engine enforces the following, independent of the LLM's output:
- **The guard is final.** Approval requires PEP success, every document-backed field cross-check matched, and no unresolved clarifications. An LLM verdict cannot override it.
- **The allowlist is `decision ∩ mode`.** The actuator for an `Escalate` verdict cannot call `approve_kyb_result`, and `Approve` under `Suggest` does not run the actuator at all.
- **The guard runs again server-side.** `approve_kyb_result` re-runs the strict guard when it is called; the engine-side guard passing is not sufficient on its own.
- **Retries are safe.** All mutating actions are idempotent. A retryable actuator failure re-runs the stateless evaluation, and the audit entry flags it (`actuator_will_retry`) so a retry reads as another attempt rather than an independent review.
- **The engine can be switched off.** A hot-reloadable flag, changed from the admin runtime-config endpoint without a redeploy, stops dispatch from claiming candidates and releases in-flight jobs, so results fall back to the human review queue.
- **Clarifications round-trip.** The guard distinguishes "awaiting the client" (a veto) from "submitted and cross-check matched." Once a clarification is submitted and matches, the actuator reviews the fields, closes the request, and may then approve.
## Audit trail
Every run writes a **`KYB_AGENT_REVIEWED`** entry to the KYB [audit trail](./index.mdx#16-ubo--director-compliance-and-audit-trail). The entry records both the model's verdict and the action the system took:
| Field | Meaning |
| -------------------- | ------------------------------------------------------------------------------- |
| **Findings count** | How many risk findings the Prosecutor raised. |
| **Judge verdict** | `Approve`, `Escalate`, or `Deny` with the Judge's rationale. |
| **Guard outcome** | Whether the deterministic guard passed or vetoed, and why. |
| **Enacted decision** | The action the actuator took (or none, under Suggest). |
| **Policy version** | The versioned policy the decision was made under (e.g. `kyb-policy-2026.06.0`). |
| **Actuator status** | Success, or a retryable failure flagged with `actuator_will_retry`. |
---
## KYB Overview
KYB verifies **businesses**, where KYC verifies individuals. A KYB flow collects a company's business information and incorporation documents, captures its **Ultimate Beneficial Owners (UBOs)** and, optionally, its **directors**, and then cross-checks the submitted data against the uploaded documents and the official company register. UBOs and directors are each verified through a linked KYC flow, and the platform runs PEP and sanctions screening before a compliance operator approves the result.
Short product explainer: [How KYB works](/docs/how-it-works/kyb).
This page walks through the full KYB lifecycle: creating a KYB flow, the experience your end users go through, and how operators review results and request clarifications.
:::info KYB result delivery
KYB results are delivered to the flow's configured webhook under `data.kyb`. The same result remains available in the Zyphe Dashboard and API. See [Webhook Payload](../webhook/webhook-payload.md#kyb-datakyb) for the field reference.
:::
## Create a KYB Flow
### 1. Start a KYB flow
Login to the Zyphe Dashboard, open the **Flows** section, and click **Create Flow** in the top right.
Open the Flows page and click Create Flow to start a new flow.
In the dialog, choose **Create KYB**.
Select Create KYB to build a flow that verifies businesses.
### 2. Configure the flow
Enter the flow's basic settings:
- **Name:** the name of the flow.
- **Slug:** a short identifier used in the verification link shared with end users. This cannot be changed after the flow is created.
- **Success URL:** where users are redirected after they complete all flow steps.
- **Webhook URL:** the endpoint that receives webhook events for the flow.
Enter the flow name, slug, success URL, and optional webhook URL.
### 3. Configure the KYB step
Configure how the business is verified:
- **KYC Flow:** each UBO (and director, when enabled) is verified with this KYC flow.
- **Maximum number of extra file uploads:** how many additional documents an applicant may attach (default 10).
- **Maximum Number of UBOs:** the maximum number of beneficial owners that can be added (default 10).
- **Collect Directors:** when enabled, the flow also collects director information during the KYB flow. Enabling it reveals a **Maximum Number of Directors** setting (default 10). See [Step 11. Directors](#11-directors-when-enabled) for the end-user experience.
- **KYB Agent Mode:** chooses how the KYB AI agent participates in this flow (disabled, suggest-only, or fully autonomous). See [KYB Agent Mode](#kyb-agent-mode) below.
- **Success Button Label:** the label of the button shown to the user on completion.
- **Required Documents:** a summary of which documents are requested, per country.
Configure the KYC flow used for UBOs, limits, director collection, agent mode, and required documents.
#### KYB Agent Mode
The KYB AI agent cross-checks every submitted business-information field against the uploaded documents and the official business register, classifies the result as a **Match**, **Partial Match (fuzzy)**, **Mismatch**, or **Not Found**, writes a one-sentence explanation, and proposes a canonical suggested value. Behind this, moderation is driven by a multi-role **[agentic review engine](./agentic-review-engine.mdx)**: analytic roles argue the case, a deterministic guard arbitrates approval, and a tool-constrained actuator enacts one audited outcome. The agent mode controls how far that automation goes:
- **Disabled:** the agent is not invoked. Fields still receive a match type, but there is no AI reasoning or suggested value, operators review everything manually.
- **Suggest:** the agent runs and surfaces its reasoning and suggested values to compliance operators **without changing the stored record**. A human confirms or rejects each one. This is what the [results screenshots](#12-business-information-verification) below show.
- **Auto-pilot:** the agent **moderates results automatically**. For fields that cleanly agree across all sources (a clean **Match**), it applies the canonical value to the business-information record with no human action. Fields that are **Partial Match**, **Mismatch**, or **Not Found**: or anything the deterministic guard vetoes, are still surfaced for manual review.
:::tip
Auto-pilot significantly reduces manual review effort by clearing the fields that already agree, while keeping every disputed field under human control.
:::
:::info
For how the engine reaches and enacts each decision (the analytic roles, the deterministic guard, the audited action, and the safety guarantees), see [KYB Agentic Review Engine](./agentic-review-engine.mdx).
:::
### 4. Configure required documents
Click **Configure documents** to define which documents applicants must provide.
- **Global documents** are requested when no country-specific override applies. By default these are the **Certificate of Incorporation**, **Articles of Association**, and **Organizational Structure Chart**, each of which can be marked Required or optional. You can also add custom documents.
- **Country-specific overrides** let you request a different document set for a given country. When a country is configured (for example, **Italy → Visura**), **only** the listed documents are requested for applicants from that country, replacing the global list.
Mark global documents as required or optional, and add country-specific document overrides.
### 5. Review the created step
Once saved, the KYB step appears in the flow, showing the configured KYC flow, limits, and document summary. Use the **Devtools** panel to access the webhook tools and **SDK Snippets** for the step.
The KYB step appears in the flow with its document summary and devtools.
:::info
For general flow settings, advanced flow building, SDK snippets, and webhook configuration, see [Create a Flow](../create-a-flow/index.mdx).
:::
## Ownership structure and recursive UBO discovery
Zyphe can discover Ultimate Beneficial Owners automatically by building and traversing the company's ownership structure. Starting with the company being verified, the system follows corporate shareholders through every available ownership tier until it reaches natural persons and calculates each person's **effective ownership** across the full chain. A person with **at least 25% direct or indirect ownership** is identified as a UBO candidate and can then complete the KYC and AML checks configured in the linked KYC flow.
For indirect ownership, each stake along a path is multiplied. For example, a person who owns 50% of a holding company that owns 60% of the subject company has 30% effective ownership (`50% × 60%`) and meets the 25% threshold.
The traversal records the source of every relationship and makes unresolved branches visible when registry coverage or available evidence does not permit the chain to be completed. This gives compliance operators both the result and the evidence path used to reach it.
In this anonymized example, the traversal covers six ownership tiers. The highest identified natural person has **23.2% effective ownership**, so no individual meets the 25% ownership test. A separate branch represents **30.2% effective ownership**, but ends at a corporate entity where registry coverage is unavailable; it is retained as a UBO proxy rather than incorrectly reported as a natural-person UBO.
### When the ownership test does not identify a UBO
When no natural person reaches the threshold, Zyphe can apply the alternative determination methodology required by the applicable regulatory framework. Depending on the configured policy and available evidence, this can include assessing **control through other means** and, when appropriate, using the company's **senior managing officials or directors** as the fallback subjects.
Under the director fallback, the selected directors are run through the configured AML checks, including PEP and sanctions screening. The system preserves the determination basis, unresolved ownership branches, and screening results so the compliance operator can review and audit why the fallback was used.
:::info
The 25% ownership test and the available fallback methodology must be applied in line with the regulations and internal policy relevant to your organization and the company being verified.
:::
## The end-user KYB experience
The following steps are what an applicant goes through after opening the KYB flow link.
### 6. Business registration status
The flow first asks whether the business is registered.
- **Yes**: the applicant continues to the business information and document steps (the registered-business path).
- **No**: the flow skips the company details and collects a single owner directly (the unregistered / sole-proprietor path).
The applicant indicates whether their business is registered.
### 7. Business information
The applicant provides the company details: **Address**, **City**, **Postal Code**, **Country**, **Company Name**, **Email**, **Company Type**, **Business Activity**, **Registration Number**, and **Incorporation Date**.
The applicant enters the registered business information.
### 8. Business documents
The applicant uploads the required company documents, by default the **Certificate of Incorporation**, **Articles of Association**, and **Organizational Structure Chart**: plus any **Other Documents**. The applicant must confirm the authenticity attestation before continuing. Uploaded documents are screened automatically.
The applicant uploads company documents and confirms they are authentic.
### 9. Ultimate Beneficial Owners
The applicant enters each Ultimate Beneficial Owner's details: **First Name**, **Last Name**, **Date of Birth**, **Gender**, **Email**, and **Country**.
The applicant provides the details of a beneficial owner.
### 10. Confirm owners
The applicant reviews the owners they have added. They can choose **Enter another UBO** (up to the configured maximum) or **Submit** to continue.
The applicant confirms the beneficial owners, adding more if needed.
### 11. Directors (when enabled)
If **Collect Directors** was enabled on the step, an equivalent director sub-flow runs **immediately after the applicant confirms the UBOs**. The director screens mirror the UBO screens above:
- A **director form** with the same fields as the UBO form (First Name, Last Name, Date of Birth, Gender, Email, Country).
- A **Confirm directors** screen, where the applicant can choose **Enter another director** (up to the configured **Maximum Number of Directors**) before submitting.
When directors are not collected, the flow submits right after the UBOs are confirmed.
:::info
After submission, each UBO **and director** completes the linked KYC flow. You can send them reminders from the dashboard (see [Step 16](#16-ubo--director-compliance-and-audit-trail)).
:::
## Reviewing KYB results
KYB results are available under **KYB Results** in the Zyphe Dashboard. Each business-information field is shown with a **Match Type** comparing the value submitted by the user, the value extracted from the documents, and the value found in the official business register.
How much of this is handled automatically depends on the **[KYB Agent Mode](#kyb-agent-mode)** chosen at setup, in Auto-pilot, agreed fields are moderated automatically and only disputed fields need operator attention.
### 12. Business information verification
Each field shows its match result, **Exact Match**, **Partial Match**, **Mismatch**, or **Not Found**. Selecting a field opens the **AI Agent reasoning** popup with the agent's explanation, a suggested value, and **All Sources**: the value submitted by the user, the value found in the documents, and the value from the business register.
Review each field's match type and the AI agent's reasoning across all sources.
### 13. Request a clarification
To resolve a discrepancy, select the field(s) that need attention and click **Send Request**.
Select the mismatched field and start a change request.
In the **Send Change Request Email** dialog, review the affected fields and add a description explaining the change you need, then **Create Request**.
Describe the change you need and send the request to the applicant.
:::info
There are two request types. A **clarification** request asks the applicant to re-confirm or update specific business-information fields (shown here), while a **standard** request asks them to upload a named document.
:::
### 14. End-user clarification
The applicant receives the request and sees the **Business Information Clarification** screen. They choose **These details are correct** or **I need to update these details**, edit the requested value, add an explanation, and submit.
The applicant confirms or updates the requested fields and submits a clarification.
### 15. Review the response
The operator sees the **Old Value** and **New Value** side by side, with the applicant's explanation, and can **Approve** or **Reject** the change per field. Until reviewed, the field shows a **Pending Review** status.
Approve or reject the applicant's updated value.
### 16. UBO & director compliance and audit trail
The result page lists the **Ultimate Beneficial Owners** and, when directors were collected, a separate **Directors** section. Both are shown the same way: a card per person with their KYC steps (Document Verification, Liveness Check, PEP, Sanctions) and a **Send reminder** action to prompt them to complete their KYC.
The **Audit Trail** records every KYB event, application submitted, clarification submitted, document and communication requests, status changes, and each **`KYB_AGENT_REVIEWED`** entry written by the [agentic review engine](./agentic-review-engine.mdx#audit-trail), with the actor, action, and event details for full compliance traceability.
Track each owner's KYC compliance and the full audit trail of KYB events.
## Statuses
A KYB result moves through several statuses during its lifecycle:
| Status type | Values |
| ----------------------- | ------------------------------------------------------------------------- |
| **Moderation status** | `ACQUIRING`, `PENDING_MODERATION`, `REVIEW_REQUIRED`, `SUCCESS`, `FAILED` |
| **PEP & sanctions** | `PENDING`, `IN_PROGRESS`, `REVIEW`, `SUCCESS`, `FAILED` |
| **Next action / stage** | `CLIENT`, `RM`, `COMPLIANCE`, `SUPPORT`, `FUNDED`, `DENIED` |
---
## Accepting Partner Invitations
## Overview
If your organization has been invited to access KYC verification results from a partner organization through KYC Passport, this guide will help you accept the invitation and start accessing shared flow results.
## Prerequisites
Before accepting a partner invitation, you need:
- An invitation email from the partner organization
- The invitation code (OTP) included in the email
- Admin access to your organization (for existing users)
- Or, ability to register a new organization (for new users)
## For Existing Organizations
If you already have an organization on the Zyphe platform, follow these steps:
### Step 1: Check Your Invitations
When you receive a partner invitation, you'll be notified through:
- Email notification with invitation details
- In-app notification in your dashboard
### Step 2: Review the Invitation
Log in to your dashboard and navigate to the **Invitations** section to view pending invites. Each invitation shows:
- The inviting organization's name
- The flow name and type
- What data will be shared
- When the invitation was sent
- Expiration date of the invitation
### Step 3: Accept or Reject
After reviewing the invitation details:
1. Click on the invitation to view full details
2. Choose to **Accept** or **Reject** the invitation
3. If accepting, enter the verification code from your email
4. Confirm your decision
:::info Access to Data
Once you accept an invitation, you immediately gain access to all flow results created from the moment the invite was sent, provided users have given consent to share data with your organization.
:::
### Step 4: Access Shared Flow Results
After accepting the invitation:
1. Navigate to the **Shared Flow Results** section in your dashboard
2. You'll see all flow results shared from partner organizations
3. Filter by partner organization or flow name
4. Click on a result to view the full verification data
## For New Organizations
If you don't have an account on the Zyphe platform yet:
### Step 1: Receive the Invitation
You'll receive an email with:
- A secure invitation link
- A verification code (OTP)
- The inviting organization's name
- Flow details that will be shared
### Step 2: Click the Invitation Link
The link in the email will direct you to the organization registration page with the invitation pre-filled.
### Step 3: Complete Organization Registration
Fill out the organization registration form with:
- Organization name
- Your admin email (pre-filled from invite)
- Password for your account
- Organization details
- Verification code from the email
### Step 4: Automatic Acceptance
Once you complete the registration:
- The invitation is automatically accepted
- Your new organization is linked to the invitation
- You immediately gain access to shared flow results
- You can start accessing verification data from the partner
### Step 5: Set Up Your Organization
After registration, you can:
- Add team members to your organization
- Configure notification preferences
- Review shared flow results
- Set up your own flows if needed
## Understanding Shared Data Access
When you accept a partner invitation, you gain access to:
### Available Data
- Verified identity documents
- Personal information (name, date of birth, address, etc.)
- Verification results and status
- Any additional data collected in the flow (forms, wallet verification, etc.)
### Data Limitations
- **Time-based access**: You can only access data from users who completed verification **after** the invite was created
- **Consent-based**: You can only view data from users who explicitly consented to share with your organization
- **Flow-specific**: Access is limited to the specific flow mentioned in the invitation
### No Access To
- Historical data from before the invitation was sent
- Data from users who declined consent
- Data from other flows not included in the invitation
## Invitation Lifecycle
### Invitation Validity
Partner invitations have an expiration period:
- Invitations are valid for a limited time (check the email for specific expiration)
- Expired invitations cannot be accepted
- Partners can resend expired invitations with a new code
### Resent Invitations
If your invitation expires or you didn't receive it:
- Contact the partner organization to resend the invitation
- You'll receive a new email with a fresh verification code
- The previous invitation code will no longer work
### Rejected Invitations
If you reject an invitation:
- The invitation is permanently closed
- No data is shared with your organization
- The partner can send a new invitation if needed in the future
## Compliance
Ensure your organization:
- Has a legal basis for processing shared data
- Provides privacy notices to end users if needed
- Respects user rights (access, deletion, etc.)
- Maintains proper data processing agreements with partners
## Need Help?
If you have questions or need assistance:
- Review the [FAQ](./faq) for common questions and troubleshooting
- Contact the inviting organization directly
- Reach out to Zyphe support
---
## Frequently Asked Questions
Common questions about KYC Passport, partner invitations, user consent, and data access.
## General Questions
### What is KYC Passport?
KYC Passport is a feature that enables organizations to securely share verified KYC data with partner organizations. It creates a trusted ecosystem where users verify once and partners can securely access the verification results with proper consent.
### How does KYC Passport benefit my organization?
KYC Passport enables you to:
- Share verification results with trusted partners
- Reduce verification friction for users in your ecosystem
- Build partner networks without compromising user privacy
- Maintain compliance while enabling data sharing
## Inviting Partners
### Can I invite multiple partners to the same flow?
Yes, you can invite as many partners as needed to a single flow. There is no technical limit on the number of partners. However, for user experience and compliance reasons, it's recommended to limit the list to partners that are essential for the user's journey.
### What happens if a user completes verification before a partner accepts the invite?
The access grant is created when the user completes verification (with consent). When the partner later accepts the invite, they gain immediate access to that data. The grant is created in an inactive state and becomes active once the partner accepts.
### Can partners access historical data?
No. Partners can only access data from users who completed verification **after** the invite was created. This ensures users have visibility into which partners their data will be shared with at the time of verification.
### What data do partners receive?
Partners receive the same KYC data that your organization has access to for the specific flow, including:
- Verified identity documents
- Personal information (name, date of birth, address, etc.)
- Verification results and status
- Any additional data collected in the flow
The exact data depends on the flow configuration and document types verified.
## Managing Invitations
### Can I see which users' data a partner has accessed?
Currently, you can see that a partner has accepted an invitation and has general access to the flow. Detailed per-user access logs are available through your organization's audit logs.
### What happens to data a partner already accessed if I revoke their access?
Revoked access prevents the partner from accessing new flow results. However, data they've already accessed and stored remains available until their retention obligations are met. Partners should follow their data retention policies for already accessed information.
### Can I temporarily suspend a partner's access?
Access revocation is permanent. If you need temporary suspension, consider creating time-limited flows or coordinating directly with the partner organization.
### Will users be notified when I revoke a partner's access?
Users are not automatically notified when partner access is revoked. If you want to inform users, you should communicate this through your own channels.
### Can I change a partner's email address after sending an invitation?
You cannot modify an existing invitation. If the email is incorrect, revoke the invitation and send a new one to the correct email address.
### How do I resend an invitation?
Find the pending invitation in the partners list, click the action menu (three dots), and select "Resend Invite". A new email with a fresh verification code will be sent. The previous code will no longer work.
## Accepting Invitations
### What if I didn't receive the invitation email?
- Check your spam/junk folder
- Verify the email address with the inviting organization
- Ask them to resend the invitation
- Check with other admins in your organization in case they received it
### Can I accept an invitation if my organization doesn't exist yet?
Yes! If you don't have an account on the Zyphe platform, the invitation will guide you through the organization registration process. Once you complete registration, the invitation is automatically accepted.
### What happens if I reject an invitation?
If you reject an invitation:
- The invitation is permanently closed
- No data is shared with your organization
- The partner can send a new invitation if needed in the future
### How long are invitations valid?
Invitations have a built-in validity period for security purposes. The exact duration is specified in the invitation email. If an invitation expires, the partner can resend it with a new verification code.
### Can multiple people in my organization accept the same invitation?
No. Each invitation can only be accepted once. The first admin to accept it will link the invitation to your organization. Other admins will have access to the shared data once the invitation is accepted.
## User Consent
### Can users revoke consent after verification?
Users cannot directly revoke consent, but they can request data deletion through your organization's data protection procedures. When a deletion request is received, you should revoke partner access as appropriate.
### What if a user wants to share data with some partners but not others?
Currently, consent is all-or-nothing for the partners listed at verification time. Consider creating multiple flows with different partner combinations if you need granular control.
### How long does user consent last?
Consent lasts for the duration specified in your privacy policy and data retention policy. Ensure these align with regulatory requirements in your jurisdiction.
### Can I add partners to a flow without getting new consent from users?
No. Partners invited after a user completes verification do not receive access to that user's data, specifically to maintain the integrity of the original consent. This ensures users always know which organizations will receive their data at the time of verification.
### How does this work with multi-step flows?
Consent is typically shown at the beginning of the flow or before the first step that collects personal information. Once provided, it applies to all data collected in that flow session.
### What happens if a user declines consent?
If a user declines consent, they cannot proceed with verification for that flow. Consider creating alternative flows without partner sharing for users who prefer not to share their data.
## Troubleshooting
### "Invalid Verification Code"
- Double-check the code from your email
- Codes are case-sensitive
- Request a new invitation if the code has expired
### "Invitation Already Used"
- This invitation has already been accepted
- Check if someone else in your organization already accepted it
- Contact the partner if you need clarification
### "Invitation Expired"
- The invitation validity period has passed
- Contact the partner organization to resend the invitation
- You'll receive a new code with extended validity
### "Email Mismatch"
- Make sure you're using the email address the invitation was sent to
- Contact the partner if the wrong email was used
- They can send a new invitation to the correct email
### "Invite has been revoked"
- The inviting organization has cancelled this invitation
- Contact them to understand why it was revoked
- They can send a new invitation if appropriate
### "Invite not for this organization"
- The invitation is linked to a different organization
- Ensure you're logged into the correct organization
- Contact the inviting organization if there's a mismatch
## Data Access & Security
### How is shared data secured?
All data is secured using:
- Cryptographic access grants
- End-to-end encryption
- Decentralized storage
Partners never receive raw access to storage, only through properly authorized access grants.
### Can partners share the data with others?
Partners should only use the data for the purposes agreed upon in your data sharing agreement. Technical limitations prevent them from creating additional partner invites for your flow data.
### What happens to my data if a partner has a security breach?
Partners are responsible for securing any data they access and store. Ensure your data sharing agreements include security requirements and breach notification procedures.
## Technical Questions
### Does KYC Passport work in sandbox mode?
Yes. All KYC Passport features are available in sandbox mode for testing. We strongly recommend testing the complete flow in sandbox before inviting production partners.
## Best Practices
### How should I choose which partners to invite?
Consider:
- Business need: Does the partner genuinely need the data?
- Trust level: Do you have a strong relationship with the partner?
- Compliance: Does the partner meet your security and privacy standards?
- User benefit: Does sharing improve the user experience?
### How many partners should I invite per flow?
Limit partners to those essential for the user's journey. Too many partners can:
- Overwhelm users during consent
- Increase compliance complexity
- Raise privacy concerns
Generally, 1-5 partners is reasonable for most use cases.
### Should I invite partners before or after creating a flow?
Ideally, invite partners after creating the flow but before users start verifying. This ensures:
- Partners appear in user consent
- Users know all recipients at verification time
- Proper access grants are created
### How often should I review partner access?
Conduct regular audits:
- Monthly reviews of active partnerships
- Quarterly compliance reviews
- Immediate review if partnerships end
- Annual comprehensive security assessments
## Still Have Questions?
If your question isn't answered here:
- Check the [technical documentation](/docs/data-flows/kyc-passport) for detailed information
- Review the individual guides:
- [Inviting Partners](./inviting-partners)
- [Managing Invitations](./managing-invitations)
- [Accepting Invitations](./accepting-invitations)
- [User Consent](./user-consent)
- Contact our support team
---
## KYC Passport
## Overview
The KYC Passport feature enables you to share verified KYC data with partner organizations, creating a trusted ecosystem where users verify once and partners can securely access the verification results with proper consent.
This section provides comprehensive guides for organizations looking to implement KYC Passport, including:
- **Inviting partner organizations** to share flow results
- **Managing partner invitations** (resend, revoke, track status)
- **Accepting partner invitations** from other organizations
- **Understanding user consent** and privacy requirements
## Prerequisites
Before using KYC Passport, ensure you have:
- An active flow created on the Zyphe platform
- Admin access to your organization
- Understanding of data sharing agreements and compliance requirements
## Quick Start
### For Organizations Inviting Partners
If you want to share your flow results with partner organizations:
1. Start with [Inviting Partner Organizations](./inviting-partners) to learn how to send invitations
2. Review [Managing Partner Invitations](./managing-invitations) to track and control partner access
3. Understand [User Consent and Privacy](./user-consent) to ensure compliance
### For Organizations Receiving Invitations
If you've been invited by a partner to access their flow results:
1. See [Accepting Partner Invitations](./accepting-invitations) for step-by-step guidance
2. Review [User Consent and Privacy](./user-consent) to understand data access rules
## Key Features
### Partner Ecosystem
Create a trusted network of partner organizations that can securely access verified user data without requiring repeated verification.
### User-Centric Consent
Users maintain full control by providing explicit consent during verification, with clear visibility into which organizations will receive their data.
### Cryptographic Security
Access grants use advanced cryptography to ensure only authorized partners can access data, with full audit trails.
### ⏱️ Time-Based Access
Partners can only access data from users who completed verification after the invitation was created, ensuring proper consent transparency.
### Access Management
Track invitation status, resend invitations, and revoke access at any time through an intuitive dashboard interface.
## How It Works
1. **Organization A** creates a KYC flow and invites **Organization B** as a partner
2. **Organization B** receives an email invitation and accepts it (or creates an account if new)
3. A **User** goes through verification on Organization A's flow
4. During the process, the **User** sees and consents to sharing data with both organizations
5. Both **Organization A** and **Organization B** can now access the user's verification results
6. Access is secured through cryptographic access grants and can be revoked at any time
## Related Documentation
- [KYC Passport Technical Documentation](/docs/data-flows/kyc-passport) - Detailed technical architecture and data flows
- [Reusable Credentials / One-Click KYC](/docs/reusable-credentials-one-click-kyc) - Enable users to reuse credentials across services
## Need Help?
If you have questions or need assistance with KYC Passport:
- Contact our support team
- Check out the dedicated [FAQ](./faq) section
- Review the [technical documentation](/docs/data-flows/kyc-passport)
---
## Inviting Partner Organizations
This guide walks you through the process of inviting partner organizations to access KYC verification results from your flow.
## Prerequisites
Before inviting partners, ensure you have:
- An active flow created on the Zyphe platform
- Admin access to your organization
- Partner organization's admin email address
- Partner organization's name
## Step 1: Navigate to Your Flow
Log in to the Zyphe Dashboard and navigate to the **Flows** section. Select the flow you want to share results from.
## Step 2: Access the Partners Section
In the flow details page, locate the **Partners** section. This is where you can manage all partner invitations for this specific flow.
The Partners section displays all invited partner organizations and their invitation status
## Step 3: Invite a New Partner
Click the **Invite Partner** button or use the action menu to open the partner invitation dialog.
## Step 4: Fill in Partner Details
In the invitation dialog, provide the following information:
- **Partner Admin Email**: The email address of the partner organization's administrator who will receive the invitation
- **Organization Name**: The name of the partner organization (this will be displayed to users during consent)
Enter the partner admin's email and organization name
## Step 5: Send the Invitation
Click **Send Invitation** to send the invite. The partner will receive an email with:
- A secure invitation link
- A verification code (OTP)
- Instructions on how to accept the invitation
## What Happens Next?
### For New Organizations
If the invited partner doesn't have an account on the Zyphe platform:
1. They receive an email invitation with a secure link and verification code
2. Clicking the link directs them to the organization registration process
3. During registration, they'll use the verification code to confirm the invitation
4. Once registered, the invitation is automatically accepted
5. The new organization immediately gains access to shared flow results (from the invite creation date forward)
### For Existing Organizations
If the partner already has an account:
1. The admin receives the invitation via email and in-app notifications
2. They can review the invitation details in their dashboard
3. They can choose to **Accept** or **Reject** the invitation
4. If accepted, they gain access to all flow results from the moment the invite was created
5. If rejected, no data is shared and the invitation is closed
Existing organizations can review pending invitations from the dashboard.
## Data Access for Partners
Once a partner accepts the invitation and users provide consent:
- Partners can view shared flow results through their dashboard
- They access data through the **Shared Flow Results** section
- Partners can only access data from users who:
- Completed verification **after** the invite was created
- Explicitly consented to share data with the partner
## Need Help?
If you encounter any issues or have questions about inviting partners:
- Check the [FAQ](./faq) for common questions
- Contact our support team
- Review the detailed [KYC Passport technical documentation](/docs/data-flows/kyc-passport)
---
## Managing Partner Invitations
After sending invitations to partner organizations, you can track their status, resend invitations, and revoke access when needed.
## Viewing Partner Invitations
All partner invitations for a flow are visible in the **Partners** section of the flow details page.
The Partners section displays all invited partner organizations and their invitation status
### Invitation Statuses
The partners list shows the status of each invitation:
- **Pending**: Invitation sent but not yet accepted by the partner organization
- **Accepted**: Partner has accepted the invitation and has access to flow results
- **Rejected**: Partner has declined the invitation (no data is shared)
Each invitation entry displays:
- Partner organization name
- Partner admin email
- Invitation status
- Date sent
- Date accepted/rejected (if applicable)
## Resending Invitations
If a partner hasn't responded to an invitation, you can resend it with a new verification code.
### When to Resend
- The partner hasn't received or can't find the original invitation
- The verification code has expired
- You want to send a reminder to the partner
### How to Resend
1. Find the pending invitation in the partners list
2. Click the action menu (three dots) next to the invitation
3. Select **Resend Invite**
4. A new email with a fresh verification code is sent to the partner
Use the context menu to resend invitations or revoke partner access
:::info
Resending an invitation generates a new verification code. The previous code will no longer work.
:::
## Revoking Partner Access
You can revoke a partner's access at any time, immediately ending their ability to view shared flow results.
### When to Revoke Access
- The partnership has ended
- The partner no longer needs access to the data
- Security or compliance concerns arise
- You want to limit the number of partners with access
### How to Revoke
1. Find the partner in the partners list (any status except rejected)
2. Click the action menu (three dots) next to the partner
3. Select **Revoke Access**
4. Confirm the revocation in the dialog
Revoke partner access through the context menu
### What Happens After Revocation
- The partner immediately loses access to all shared data for that flow
- They will no longer see the flow in their "Shared Flow Results" section
- The invitation status changes to "Revoked"
- Access to historical data they've already accessed remains until retention obligations are met
:::warning Important
Revoking access is permanent. The partner organization will need to be invited again if access should be restored in the future. Note that if access is revoked, it remains available until the partner's data retention obligations are met.
:::
## Need Help?
If you have questions about managing partner invitations:
- Check the [FAQ](./faq) for common questions
- Contact our support team
- Review the [KYC Passport technical documentation](/docs/data-flows/kyc-passport)
---
## User Consent and Privacy
User consent is central to the KYC Passport functionality. This guide explains how user consent works, what users see, and important compliance considerations.
## Overview
When users go through verification on a flow with partner sharing enabled, they must provide explicit consent before their data can be shared with partner organizations.
## The Consent Process
### When Consent is Requested
During the verification flow, users encounter a privacy consent step that clearly discloses:
1. **The main organization** they're verifying with
2. **All partner organizations** that will receive their data
3. **The types of data** that will be shared
4. **Why the data is being shared** (optional, based on your configuration)
### What Users See
Users see a clear consent screen listing all organizations that will have access to their verification data:
Users see a clear list of all organizations that will receive their data during the consent step
The consent screen includes:
- **Organization names**: Your organization and all invited partner organizations
- **Data types**: What information will be shared (identity documents, personal information, verification results)
- **Consent checkbox**: Users must explicitly check this to proceed
- **Clear language**: Easy-to-understand explanation of data sharing
### User Decision
Users have two options:
1. **Provide consent**: Check the consent box and proceed with verification
- Their data will be shared with your organization and all accepted partner organizations
- Access grants are created for authorized partners
- Verification continues normally
2. **Decline consent**: Not check the consent box
- They cannot proceed with verification
- No data is shared with any organization
- No access grants are created
:::warning Important
If a user declines consent, they cannot complete the verification process. Consider creating separate flows without partner sharing for users who prefer not to share their data.
:::
## Consent Rules and Timing
### No Consent = No Access
If a user does not consent to data sharing:
- No access grants are created for any partner organizations
- Only metadata (non-PII) may be stored for analytics
- Partners cannot access any verification data
- The user must start over if they later decide to consent
### Invite Timing Matters
The consent shown to users is based on the partners invited **at the time of verification**:
- Partners invited **before** user verification are included in the consent
- Partners invited **after** user verification do NOT receive access to that user's data
- This ensures users always know which organizations will receive their data
**Example Timeline:**
```
Day 1: Organization invites Partner A and Partner B
Day 2: User completes verification (sees A and B in consent)
Day 3: Organization invites Partner C
Result: Partners A and B can access the user's data
Partner C CANNOT access this user's data
```
### Consent is Flow-Specific
Each flow has its own partner list and consent:
- Users consent to share data for a **specific flow**
- They consent to share with the **partners listed at that time**
- Consent for one flow does not apply to other flows
- Users must consent separately for each flow they complete
## Access Grant Creation
When a user consents and completes verification, the system creates cryptographic access grants:
### Grant Creation Rules
1. **Main Organization**: Always receives an access grant when the user completes verification with consent
2. **Accepted Partner Invites**: If the partner has already accepted the invite before the user verified, they receive an access grant immediately
3. **Pending Partner Invites**: An access grant is created but remains inactive until the partner accepts the invite
4. **Rejected Partner Invites**: No access grant is created for partners who rejected invitations
5. **No Consent**: If the user doesn't consent, no partner grants are created (only the main organization receives a grant if verification proceeds through alternative means)
### Grant Lifecycle
```mermaid
sequenceDiagram
actor User
participant BE as Backend
participant MO as Main Org
participant PA as Partner A(Accepted)
participant PB as Partner B(Pending)
User->>+BE: Complete verificationwith consent
BE->>MO: Create access grant
BE->>PA: Create active grant
BE->>PB: Create inactive grant
Note over PB: Later accepts invite
PB->>+BE: Accept invitation
BE->>PB: Activate grant
```
## Need Help?
If you have questions about user consent and KYC Passport:
- Check the [FAQ](./faq) for common questions
- Contact our support team
- Consult with your legal/compliance team
- Review the [KYC Passport technical documentation](/docs/data-flows/kyc-passport)
---
## Manual Review
The Manual Review process ensures that verification results requiring human intervention are properly evaluated by authorized personnel before a final disposition is reached.
## How It Works
1. **Flagging:** When a verification result (DV or KYB) cannot be automatically determined, it is assigned the `REQUIRES_MANUAL_REVIEW` status.
2. **Notification:** The system triggers a `REVIEW` webhook event to notify the organization.
3. **Review:** An authorized reviewer accesses the dashboard to inspect the result, including AI-generated suggestions and confidence scores.
4. **Decision:** The reviewer submits a final status (Approved, Rejected, or Failed).
5. **Processing:** A background worker processes the decision and triggers subsequent workflow actions and webhooks.
## Review Statuses
| Status | Description |
| :----------- | :-------------------------------------------------------------------------- |
| **Approved** | The result is verified as valid and the flow continues. |
| **Rejected** | The result is determined to be fraudulent or invalid. |
| **Failed** | The verification process could not be completed (e.g., poor image quality). |
## Permissions
Access to manual review operations is governed by the following permission levels:
- **ORG_OPERATOR:** Can review results currently in the `RequiresManualReview` status.
- **ORG_ADMIN:** Can review results in the `RequiresManualReview` status.
- **Platform Admin:** Can review results in `RequiresManualReview`, `RequiresAdminReview`, `Partial`, and `Failed` statuses.
## Webhook Events
- **REVIEW:** Sent immediately when a result is flagged for manual review.
- **COMPLETED / FAILED / REJECTED:** Sent after the manual review decision has been processed, reflecting the final state of the verification.
## Related Documentation
- [Webhook Integration](/docs/guides/webhook)
- [Webhook Payload](/docs/guides/webhook/webhook-payload)
---
## Model Context Protocol (MCP)
Zyphe ships a built-in [Model Context Protocol](https://modelcontextprotocol.io) server that lets AI agents and MCP-compatible clients (Claude Desktop, Claude Code, Cursor, and any other MCP host) manage your **flows**, **forms**, and **API keys** programmatically through natural language.
Instead of writing REST clients or learning the dashboard UI, you can simply ask your AI assistant _"create a new KYC flow with a document verification step and a form"_. The agent will call the right MCP tools on your behalf.
:::info
The MCP server is exposed by the same Zyphe API server that handles REST traffic. There is no separate process or port to run.
:::
## Capabilities at a glance
The MCP server exposes **19 tools** organized into four groups:
- **Flows**: list, create, read, update, delete flows; manage flow-level settings (branding, webhooks).
- **Flow steps**: add, update, and delete steps inside a flow (DV, FORM, POA, KYB, SPID, PHONE, WALLET, LIVENESS, AUTO_ONE_CLICK_KYC, DOCUMENT_SELECTION, GEOLOCATION).
- **Forms**: list, create, read, update, delete forms with their sections.
- **API keys**: list, create, read, update, delete API keys (SECRET and PUBLISHABLE).
Every tool accepts an optional `sandbox: boolean` flag so you can target the sandbox environment from the same connection.
## Authentication
The MCP server is authenticated with **bot tokens**. Bots are organization-scoped service identities; their permissions are evaluated by the same role-based access control policies that govern the REST API, so a bot can only do what its assigned role is allowed to do.
### 1. Create a bot and obtain a token
Bot management lives under `/organizations/{organization_id}/bots/...` in the REST API:
Create and manage bot identities from the Bots page in the dashboard.
| Method | Path | Purpose |
| -------- | -------------------------------------------------------- | ---------------------------------------- |
| `GET` | `/organizations/{org_id}/bots/list` | List bots in the organization |
| `POST` | `/organizations/{org_id}/bots/create` | Create a new bot (returns plain token) |
| `GET` | `/organizations/{org_id}/bots/{bot_id}/get` | Fetch bot metadata |
| `POST` | `/organizations/{org_id}/bots/{bot_id}/regenerate-token` | Issue a new token; the previous one dies |
| `DELETE` | `/organizations/{org_id}/bots/{bot_id}/delete` | Soft-delete the bot |
The plain-text token is returned **only once** at creation (or regeneration). It looks like:
Create a bot to obtain a token that MCP-compatible clients can use.
```
zyphe_bot_<48 random alphanumeric characters>
```
:::warning Store the token securely
Tokens are SHA-256 hashed on the server, so Zyphe cannot retrieve a lost token. If you misplace one, regenerate it. Treat bot tokens like passwords: never commit them to source control, never paste them into chat logs.
:::
### 2. Use the token
Send it as a `Bearer` token on the `Authorization` header:
```http
Authorization: Bearer zyphe_bot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
If the header is missing or the token is invalid, the server responds with `401 Unauthorized`. If the bot's role does not allow the requested operation, the tool call returns an MCP error with code `INVALID_REQUEST` and message `"Permission denied"`.
## Endpoint
The MCP server is mounted on the same host and port as the Zyphe API:
```
POST {ZYPHE_API_BASE_URL}/mcp
```
It uses the **Streamable HTTP** transport from the MCP specification. Any MCP-compliant client that supports HTTP transport can connect to it.
## Connecting a client
### Claude Desktop / Claude Code
Add an entry to your `mcp.json` (or the equivalent config UI):
```json
{
"mcpServers": {
"zyphe": {
"transport": {
"type": "http",
"url": "https://api.zyphe.com/mcp",
"headers": {
"Authorization": "Bearer zyphe_bot_YOUR_TOKEN_HERE"
}
}
}
}
}
```
Replace `https://api.zyphe.com` with the URL of your Zyphe deployment, and substitute your bot token.
### Cursor
Cursor supports MCP via the same JSON config format. Add the snippet above to your Cursor MCP settings.
### Generic / programmatic
Any MCP client SDK (TypeScript, Python, etc.) that supports the streamable-HTTP transport can talk to `/mcp`. Point the transport at the URL and inject the `Authorization` header.
## Tool reference
All parameters use **camelCase** keys. Every tool also accepts an optional `sandbox` boolean (default `false`) to target the sandbox environment instead of production.
### Flows
#### `list_flows`
List flows for the bot's organization, with optional pagination, sorting, and filtering.
| Field | Type | Required | Description |
| --------- | ------- | -------- | -------------------------------------------------------------------------------- |
| `skip` | integer | no | Records to skip (default 0) |
| `take` | integer | no | Records to return (default 100) |
| `sort` | string | no | Sort field and direction, e.g. `"createdAt:desc"` |
| `filter` | string | no | Semicolon-separated triples `"field:operator:value"` (e.g. `"flow_type:eq:KYC"`) |
| `sandbox` | boolean | no | Use sandbox environment |
#### `create_flow`
Create a new flow. **Required:** `name`, `slug`, `webhookUrl`, `successUrl`.
| Field | Type | Description |
| ------------ | ------- | --------------------------------------- |
| `name` | string | Display name of the flow |
| `slug` | string | URL-safe identifier (e.g. `"kyc-flow"`) |
| `webhookUrl` | string | Webhook URL called on flow completion |
| `successUrl` | string | Redirect URL on successful completion |
| `failureUrl` | string | Redirect URL on failure (optional) |
| `sandbox` | boolean | Create in sandbox environment |
#### `update_flow`
Update an existing flow's metadata. **Required:** `flowId`, `name`, `slug`, `webhookUrl`, `successUrl`.
#### `get_flow`
Get a flow by its ID. **Required:** `flowId`.
#### `delete_flow`
Soft-delete a flow by its ID. **Required:** `flowId`.
#### `get_flow_settings`
Get the settings (branding, webhook options) for a flow. **Required:** `flowId`.
#### `update_flow_settings`
Update the settings for a flow. **Required:** `flowId`. All other fields are optional and patch-style; only provided fields are changed.
| Field | Type | Description |
| ------------------ | ------- | ---------------------------------------------- |
| `primaryColor` | string | Primary brand color (hex) |
| `secondaryColor` | string | Secondary brand color (hex) |
| `profileImage` | string | URL of the organization logo |
| `webhookSecret` | string | HMAC secret for webhook signature verification |
| `extendedWebhook` | boolean | Include extended data in webhook payloads |
| `published` | boolean | Whether the flow is publicly accessible |
| `skipSignedAccess` | boolean | Skip signed-URL access control |
### Flow steps
#### `create_flow_step`
Add a new step to a flow. **Required:** `flowId`, `name`, `stepType`, `config`, `order`.
| Field | Type | Description |
| ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `flowId` | string | UUID of the parent flow |
| `name` | string | Display name of the step |
| `stepType` | string | One of: `DV`, `FORM`, `POA`, `KYB`, `SPID`, `PHONE`, `WALLET`, `LIVENESS`, `AUTO_ONE_CLICK_KYC`, `DOCUMENT_SELECTION`, `GEOLOCATION` |
| `config` | object | Step configuration object (must contain a matching `stepType` discriminator) |
| `order` | integer | Position of the step in the flow (0-based) |
| `successUrl` | string | Override success redirect URL for this step (optional) |
| `failureUrl` | string | Override failure redirect URL for this step (optional) |
:::tip
Each step type has its own `config` schema. See the [flow-step guides](/docs/guides/create-a-flow/) for the shape of each step's configuration.
:::
#### `update_flow_step`
Update a step's name, config, or redirect URLs. **Required:** `flowId`, `stepId`, `config`.
#### `delete_flow_step`
Soft-delete a step from a flow. **Required:** `flowId`, `stepId`.
### Forms
#### `list_forms`
List forms for the bot's organization, with optional pagination and sorting. Same pagination fields as `list_flows` (without `filter`).
#### `create_form`
Create a new form. **Required:** `name`, `sections`.
| Field | Type | Description |
| ---------- | ------ | ------------------------------ |
| `name` | string | Display name of the form |
| `sections` | array | Array of `FormSection` objects |
#### `update_form`
Update a form's name and/or sections. **Required:** `formId`.
#### `get_form`
Get a form by its ID. **Required:** `formId`.
#### `delete_form`
Hard-delete a form by its ID. **Required:** `formId`.
### API keys
#### `list_api_keys`
List API keys for the bot's organization.
#### `create_api_key`
Create a new API key. **Required:** `keyType`. The plain-text key is returned **once** in the `key` field of the response, so store it immediately.
| Field | Type | Description |
| ---------------- | ---------------- | ----------------------------------- |
| `keyType` | string | `SECRET` or `PUBLISHABLE` |
| `name` | string | Human-readable name (optional) |
| `description` | string | Description (optional) |
| `allowedOrigins` | array of strings | Allowed CORS origins (optional) |
| `options` | object | Additional options as a JSON object |
#### `get_api_key`
Get an API key by its ID. **Required:** `apiKeyId`.
#### `update_api_key`
Update an API key's name, description, allowed origins, or options. **Required:** `apiKeyId`.
#### `delete_api_key`
Soft-delete an API key by its ID. **Required:** `apiKeyId`.
## Sandbox vs production
Every tool takes an optional `sandbox` boolean. When `true`, the tool reads and writes against the sandbox dataset; when `false` or omitted, it operates on production data.
This means a single bot token can drive both environments, which is useful for agents that build a flow in sandbox, validate it, and then promote it to production in one session.
## Error handling
MCP errors follow the standard MCP error model:
| Condition | Error code | Notes |
| ------------------------------------ | ------------------ | ------------------------------------------------ |
| Missing or malformed parameters | `INVALID_PARAMS` | Returned when JSON parsing or schema check fails |
| Unknown tool name | `METHOD_NOT_FOUND` | Tool not registered on this server |
| RBAC policy denial | `INVALID_REQUEST` | Message: `"Permission denied"` |
| Unexpected backend / DB / RBAC error | `INTERNAL_ERROR` | Server-side failure; check Zyphe logs |
HTTP-level failures (`401`, `403`) are returned **before** the MCP envelope and indicate problems with the bot token or middleware, not with a specific tool call.
## Example session
A typical agent conversation with the Zyphe MCP server might look like this (paraphrased; the agent translates intent into tool calls):
> **You:** Create a sandbox flow called "Onboarding Test" with a document verification step.
>
> **Agent:**
>
> 1. calls `create_flow` with `{ name: "Onboarding Test", slug: "onboarding-test", webhookUrl: "...", successUrl: "...", sandbox: true }`
> 2. takes the returned `flowId` and calls `create_flow_step` with `{ flowId, name: "DV", stepType: "DV", config: { stepType: "DV", ... }, order: 0, sandbox: true }`
> 3. returns: _"Created sandbox flow `onboarding-test` (id `…`) with one DV step at position 0."_
## Security notes
- Bot tokens authenticate at the request level; the MCP transport does **not** add a separate session layer.
- All tool calls are subject to the **same role-based access control policies** that govern the REST API. A bot scoped to a specific role cannot escalate privileges through MCP.
- Token hashes are stored with SHA-256; plain-text tokens are never persisted.
- Regenerating a bot's token instantly invalidates the previous one. Use this if you suspect leakage.
---
## Sandbox and go-live
Use this checklist after you complete the [Quickstart](/docs/quickstart). Work entirely in **sandbox** until every item passes, then switch environments for production.
---
## Sandbox testing
### Account and access
- [ ] Organization exists and you can sign in to the [dashboard](/docs/guides/dashboard)
- [ ] Dashboard is in **sandbox** mode
- [ ] At least one **Admin** or **Developer** can manage flows and API keys, [roles](/docs/guides/manage-users)
- [ ] Reviewers (Operator / AML Officer) can open flow results without over-privileged access, [PII access](/docs/pii-access-control)
### Flow and happy path
- [ ] A KYC [flow](/docs/guides/create-a-flow) exists with the steps you need (for example Document Verification + Liveness)
- [ ] You can complete a full verification yourself in sandbox
- [ ] Flow result status reaches a terminal state you understand, [statuses](/docs/reference/statuses-and-codes)
- [ ] Public link works if you use hosted UI, [sharing](/docs/guides/create-a-flow/sharing)
- [ ] SDK path works if you embed, [SDKs](/docs/guides/sdk)
### Webhooks
- [ ] Webhook URL is HTTPS and reachable from the internet (or a tunnel in local dev)
- [ ] Signature verification uses the **raw body** and organization secret, [signature](/docs/guides/webhook/webhook-signature)
- [ ] Handler returns `200 OK` quickly; heavy work is async
- [ ] Handler is **idempotent** (retries are expected)
- [ ] You tested `COMPLETED`, `FAILED`, and `REVIEW` (if manual review is enabled), [failure and review](/docs/guides/webhook/failure-payload)
- [ ] You reconciled events with your own user id via [custom fields](/docs/guides/webhook/custom-fields)
- [ ] Optional: use flow developer tools to trigger and monitor deliveries before real traffic
### Review and scoring
- [ ] Operators can approve / reject results, [manual review](/docs/guides/manual-review)
- [ ] Scores appear as expected if configured, [scoring](/docs/guides/scoring)
- [ ] Your product **does not reject on `REVIEW`**; it waits for the final event
### Optional products (if in scope)
- [ ] **KYB** sandbox application completes and is reviewable, [KYB](/docs/guides/kyb)
- [ ] **AML** screening results and risk score are understood, [AML](/docs/aml)
- [ ] **Transaction monitoring** rules evaluate sandbox events with Allow / Review / Block, [TM](/docs/guides/transaction-monitoring)
- [ ] **KYC Passport** invitation path tested with a partner org, [KYC Passport](/docs/guides/kyc-passport)
- [ ] **MCP / bot token** can list or create flows only where you intend automation, [MCP](/docs/guides/model-context-protocol-mcp)
---
## Go-live checklist
### Credentials and environments
- [ ] Create **production** [API keys](/docs/guides/api-keys); never reuse sandbox keys in production clients
- [ ] Store secrets in a server-side secret manager (API keys, webhook secret, bot tokens)
- [ ] Confirm environment selection in the Node SDK / server config (`staging` vs production as applicable)
- [ ] Rotate any key that was ever committed or shared in chat
### Webhooks and networking
- [ ] Production webhook URL points at production infrastructure
- [ ] Signature verification enabled and failing closed (reject invalid signatures)
- [ ] Timeouts and retries in your queue are sensible; endpoint stays under delivery limits
- [ ] Alerting exists for webhook processing failures
### Product configuration
- [ ] Production flows mirror sandbox (steps, thresholds, success URL, branding), [theming](/docs/guides/theming)
- [ ] Country / document filters match your compliance policy
- [ ] Minimum age and score thresholds reviewed with compliance
- [ ] Manual review routing and reviewer roles assigned
- [ ] Billing products enabled for what you will charge, [billing](/docs/guides/billing)
### Privacy and access
- [ ] Role assignments follow least privilege, [Manage users](/docs/guides/manage-users)
- [ ] Extended webhook / PII fields only enabled where contracts and access control allow
- [ ] Data retention and deletion process understood, [data deletion](/docs/data-flows/data-deletion)
### Observability
- [ ] You log `resultId`, verification request id, and your own external user id for support
- [ ] You can map webhook statuses using [Statuses and codes](/docs/reference/statuses-and-codes)
- [ ] Runbook exists for `REVIEW` backlog and elevated failure rates
### Cutover
- [ ] Smoke-test one real production verification with a controlled account
- [ ] Confirm production webhook delivery and dashboard result
- [ ] Switch client traffic from sandbox links / keys to production
- [ ] Keep sandbox for ongoing QA and rule backtests
---
## Recommended order
1. [Quickstart](/docs/quickstart), first reviewed result in sandbox
2. This page, full sandbox matrix for your product scope
3. [Choosing an integration method](/docs/choosing-an-integration-method), lock public link vs SDK vs MCP
4. Production credentials + webhook + smoke test
5. Enable additional products (AML, TM, Passport) with the same sandbox-first habit
## Related
- [Webhook integration](/docs/guides/webhook)
- [API reference](/docs/openapi/zyphe-sdk)
- [FAQ](/docs/frequently-asked-questions)
---
## How Scores Are Computed
Every enabled score definition produces one outcome per flow result. The computation runs when the flow completes and again on every re‑evaluation, and each outcome is frozen with the tags and factors that contributed to it, so a score can always be re‑explained and audited.
For each score definition, the engine runs a three-step pipeline:
```mermaid
flowchart LR
A["Matched tags+ contextual factors"] --> B["1 · Self score(normalization strategy)"]
B --> C["2 · Combine with sources(combination strategy)"]
C --> D["3 · Clamp & roundinteger 0–100"]
D --> E["LevelLow · Medium · High · Critical"]
```
Scores are evaluated in dependency order, so by the time a score is computed, all of its sources already have a value.
## Inputs
- **Score tags**: weighted labels matched on the flow result. Tags come from form answers, workflow actions, and manual assignment. A tag's **weight** expresses how strongly it pushes the score up.
- **Contextual factors**: multipliers that amplify the tag-based result when a contextual signal is present (for example a specific answer value or jurisdiction).
- **Sources**: the already-computed results of other scores folded into this one.
| Value | Range | Notes |
| ----------------- | ----------------------------- | --------------------------------------------------------------------------- |
| Tag weight | `-1000` to `100` | Positive contributes; `0` matches but adds nothing; negative acts as a veto |
| Factor multiplier | greater than `-1`, up to `10` | `0.5` amplifies the base result by 50% |
| Score output | integer `0` to `100` | Always clamped and rounded |
## Step 1: Self score (normalization strategies)
The normalization strategy defines how the weights of the matched tags are turned into a percentage.
### Additive share (scorecard): default
Classic scorecard math. Tags are organized in **groups**: all the options of one form question form a group (they are mutually exclusive), while a standalone tag is its own group. The score is the share of points obtained out of the points obtainable:
```text
base = ( Σ matched weights / Σ per-group maximum active weight ) × 100
```
Selecting the highest-weighted option of every scored question yields 100%; maxing out one question of two equally weighted ones yields 50%.
**Veto tags:** a tag with a _negative_ weight never raises the obtainable maximum, but when matched it subtracts its full magnitude from the obtained points. A strong veto (e.g. `-1000`) forces the score to `0` regardless of the other answers. Vetoed results are not amplified by contextual factors.
### Relative share
Scale-invariant: each matched tag contributes its weight as a share of the highest active weight in the definition (`s = w / w_max`), and shares are combined with a probabilistic union:
```text
base = ( 1 − ∏ (1 − sᵢ) ) × 100
```
A single tag at the maximum weight saturates the score to 100%; additional matches push the score asymptotically toward 100% without ever exceeding it.
### Single tag ceiling
The legacy strategy used by AML screening. Each matched weight contributes `w · ln(1 + w)`, the sum is amplified by contextual factors, and the result is normalized against a worst-case ceiling: the largest single scaled weight amplified by the maximum configured multiplier of each factor type.
## Contextual factor amplification
After the base result is computed, the multipliers of all matched contextual factors are summed and applied:
```text
amplified = base × ( 1 + Σ multipliers )
```
Amplification never lowers a _Relative share_ result, and it is skipped entirely for vetoed _Additive share_ results, so a negative factor can never flip a veto.
## Step 2: Combine with sources
If the definition lists **Sources**, its own tag-based result is folded together with the source scores using the combination strategy:
Probabilistic union (default)
100 × ( 1 − ∏ (1 − sᵢ / 100) ), each contribution increases the score, with diminishing returns as it approaches 100
Maximum
The highest of the contributing scores
Weighted
The equal-weight mean of the contributing scores
## Step 3: Clamp and level
The combined result is clamped to `[0, 100]`, rounded to an integer, and mapped to a level: `0–25` Low, `26–50` Medium, `51–75` High, `76–100` Critical. The default score's percentage is also surfaced as the flow result's headline **Risk score**.
## Worked example
A form has two scored select questions feeding a custom `supplier_risk` score with _Additive share_ normalization:
- **Company age**: "Less than 1 year" (weight `30`), "1–5 years" (weight `10`), "Over 5 years" (weight `0`)
- **Prior incidents**: "Yes" (weight `50`), "No" (weight `0`)
The obtainable maximum is `30 + 50 = 80`. A supplier answering "Less than 1 year" and "No" obtains `30`, so the base score is `30 / 80 × 100 = 37.5`. A contextual factor for a high-risk jurisdiction with multiplier `0.2` amplifies it to `37.5 × 1.2 = 45`, which rounds to **45 (Medium)**.
:::tip Configure it
Tags and their weights are managed on the [Score Definitions](/docs/guides/scoring/score-definitions) page, and attached to questions in the [form builder](/docs/guides/create-a-flow/form#scoring).
:::
---
## Scoring
Zyphe includes a generalized scoring engine that turns the signals collected during a verification flow into one or more normalized **scores**. Every score is an integer percentage from `0` to `100`, paired with a categorical **level** (_Low_, _Medium_, _High_, or _Critical_), and is computed per flow result.
Out of the box, every organization has a default **Risk score** that consolidates AML screening results, form answers, workflow actions, and manually assigned tags. On top of that, you can define your own custom scores, for example a `supplier_risk` or `onboarding_quality` score, each fed by its own set of weighted tags.
A common use of scores is grading form answers: in the form builder you can assign weights to a question's options and answers, and those weights feed the scores computed for the flow result. See [Form scoring](/docs/guides/create-a-flow/form#scoring) for how to set it up.
## Where score signals come from
Form answers
Weighted score tags and contextual factors attached to form fields in the form builder, see{' '}
Form scoring
Workflow actions
Tags assigned by the Assign Risk Tags action node in the flow builder
AML screening
The AML risk score computed from sanctions, PEP, and adverse media matches
Manual review
Score tags assigned or retracted by reviewers directly on a flow result
## Score levels
Each score percentage maps to a level using fixed thresholds:
| Score range | Level |
| ----------- | -------- |
| `0`, `25` | Low |
| `26`, `50` | Medium |
| `51`, `75` | High |
| `76`, `100` | Critical |
In the dashboard, the default Risk score is rendered as a color‑coded badge with its level; custom scores are displayed neutrally as a percentage.
## How scoring fits together
```mermaid
flowchart LR
A["Configurescore definitions · tags · form scoring"] --> B["Collectuser completes the flow"]
B --> C["Computeone outcome per enabled score"]
C --> D["ReviewScores tab · re-evaluate · manual tags"]
```
1. **Configure**: define custom scores and their tags on the [Score Definitions](/docs/guides/scoring/score-definitions) page, and attach score weights to form questions in the [form builder](/docs/guides/create-a-flow/form#scoring).
2. **Collect**: as the end user completes the verification flow, their answers and the flow's actions emit tags and contextual factors onto the flow result.
3. **Compute**: when the flow completes (or is re‑evaluated), each enabled score is calculated from its matched tags, factors, and source scores. See [How Scores Are Computed](/docs/guides/scoring/how-scores-are-computed).
4. **Review**: reviewers see every computed score on the flow result detail page, can assign or retract tags manually, and can trigger a re‑evaluation. See [Reviewing Scores](/docs/guides/scoring/reviewing-scores).
---
## Reviewing Scores
Computed scores are surfaced on the flow result detail page in the Zyphe Dashboard, in a dedicated **Scores** tab that appears once a result carries score tags.
The Scores tab shows every computed score for the flow result, along with the score tags that produced them.
## Score badges
Each enabled score is displayed with its current value:
- The default **Risk score** is shown as a color‑coded badge with both percentage and level (e.g. `62% · High`).
- **Custom scores** are shown neutrally as a percentage, since their meaning is organization-specific.
- Scores fed by a specific form indicate the form they were computed from.
## Assigned score tags
Below the badges, the **Assigned score tags** section lists every tag currently attached to the result together with its weight, whether it was emitted by a form answer, a workflow action, or a reviewer.
Reviewers with the appropriate permissions can:
- **Assign a tag manually**: click **Add tag**, pick the score it feeds, then pick the tag. This is useful to encode findings from a manual check into the score.
- **Retract a tag**: remove a previously assigned tag from the result.
Manually assign a score tag to fold a reviewer's finding into the score.
## Re-evaluation
Scores are computed automatically when the flow completes, and recomputed when their inputs change, for example after an AML moderation decision or a manual tag assignment. Reviewers can also trigger a recomputation explicitly with the **Re-evaluate** button on the Scores tab.
When a re‑evaluation changes the overall score or the result's status, Zyphe can notify your registered webhook endpoint with the before/after values, so downstream systems stay in sync. See [Webhook](/docs/guides/webhook) for the general webhook setup.
:::info Permissions
Viewing scores requires access to flow results; assigning or retracting tags manually is reserved to **Admin** users, and re-evaluation is available to **Admin**, **Operator**, and **AML Officer** users. See [Manage Users](/docs/guides/manage-users).
:::
:::tip Where scores come from
Score tags are authored on the [Score Definitions](/docs/guides/scoring/score-definitions) page and attached to questions in the [form builder](/docs/guides/create-a-flow/form#scoring). The math behind the percentages is described in [How Scores Are Computed](/docs/guides/scoring/how-scores-are-computed).
:::
---
## Score Definitions
A **score definition** describes one score your organization computes for each flow result: its name, the strategy used to calculate it, and the set of weighted tags that feed it.
In the Zyphe Dashboard, navigate to the **Score Definitions** page in the **Compliance** section to define custom scores for your organization and manage the tags that feed them.
:::info Permissions
Score definitions are managed by **Admin** users. See [Manage Users](/docs/guides/manage-users) for the full role reference.
:::
The Score Definitions page lists the system Risk score alongside your organization's custom scores.
## System scores
Every organization starts with system‑provided definitions, most notably the default **Risk** score, which is the headline score shown on flow results. System definitions cannot be edited or removed, and their keys (`risk`, `aml`) are reserved: you cannot create a custom definition with those keys. You can, however, manage the custom tags that feed them.
## Create a custom score
Click **New definition** and fill in:
- **Name:** The user-facing name of the score.
- **Key:** A unique, immutable identifier (lowercase letters, digits, and underscores, e.g. `supplier_risk`). The key cannot be changed after creation.
- **Description:** An optional description of what the score measures.
- **Combination strategy:** How the score's own tag-based result is combined with its sources, **Probabilistic union** (default), **Maximum**, or **Weighted**.
- **Normalization:** How matched tag weights are turned into a percentage, **Additive share (scorecard)** (default), **Relative share**, or **Single tag ceiling**.
- **Sources:** Other scores folded into this one. A score can build on the result of other scores; circular references are rejected.
The strategies are explained in detail in [How Scores Are Computed](/docs/guides/scoring/how-scores-are-computed).
Create a custom score definition with its key, combination strategy, normalization, and sources.
Existing definitions can be renamed, re-described, re-configured, or disabled with the **Enabled** checkbox, a disabled score is skipped at computation time without deleting its configuration.
## Manage tags
Each definition has a **Manage tags** action that opens the list of tags feeding that score, with their weight and status.
- **Add a tag:** Provide the **Tag** name and its **Weight**. The tag can then be assigned by forms and workflow actions.
- **Edit a tag:** Adjust the **Weight** or toggle the tag **Active**/inactive without deleting it.
Manage the weighted tags that feed a score definition.
:::tip Scoring form answers
The most common way to feed a custom score is directly from a form: assign weights to answers and options in the form builder, and they are recorded as score tags on the flow result. See [Form scoring](/docs/guides/create-a-flow/form#scoring).
:::
---
## Transaction Monitoring
Zyphe's Transaction Monitoring watches payment activity in real time. Your integration reports each payment event (deposit, withdrawal, or transfer) to Zyphe, the event is evaluated synchronously against your organization's enabled monitoring rules, and the response carries an **Allow**, **Review**, or **Block** decision, before your platform moves any funds.
This page covers how to **operate** Transaction Monitoring from the dashboard. For the conceptual overview (where it sits in full-cycle compliance and how the decision model relates to Zyphe's scoring engine), see [Transaction Monitoring](/docs/transaction-monitoring) in the Overview.
## How it works
```mermaid
sequenceDiagram
autonumber
participant APP as Your application
participant ZB as Zyphe Backend
participant RE as Rule engine
APP->>+ZB: Report transaction event
ZB-->>ZB: Resolve verified identity and counterparty
ZB-->>ZB: Enrich with derived fields and list matches
ZB->>+RE: Evaluate enabled rules
RE-->>-ZB: Fired rules + combined P(risk)
ZB-->>ZB: Apply thresholds, persist, raise alerts
ZB->>-APP: Allow / Review / Block + fired-rule provenance
ZB--)APP: Transaction alert webhook (if alerts raised)
```
1. Your application reports the transaction event, referencing the verified identity it belongs to.
2. Zyphe resolves the identity and counterparty, and enriches the event with derived fields (hour of day, business hours, country of the counterparty's IBAN, …) and matches against your [managed lists](/docs/guides/transaction-monitoring/lists).
3. Every enabled [rule](/docs/guides/transaction-monitoring/rules-and-templates) is evaluated. Each rule that fires contributes a probability, its confidence that the transaction is risky.
4. The contributions are combined into a single `P(risk)` with a probabilistic union (noisy-OR):
```text
P(risk) = 1 − ∏ (1 − pᵢ)
```
5. `P(risk)` is compared against your organization's thresholds to produce the decision, which is returned synchronously so your platform can hold or reject the payment inline.
6. Rules flagged with **Raise alert** additionally create a review-queue alert and notify your webhook.
| Condition | Decision |
| ------------------------------------ | -------- |
| `P(risk)` below the review threshold | Allow |
| `P(risk)` ≥ review threshold | Review |
| `P(risk)` ≥ block threshold | Block |
Both thresholds are configurable per organization. See [Settings and Alerts](/docs/guides/transaction-monitoring/settings-and-alerts).
## What you can do
- **Screen every payment in real time**: get a synchronous Allow / Review / Block verdict with full fired-rule provenance, and decide inline whether to execute, hold, or reject.
- **Author your own rules**: build boolean or banded-metric rules over transaction, list, and identity fields with a visual condition builder; every rule change is versioned and auditable.
- **Start from a regulator-informed catalog**: instantiate jurisdiction-tagged rule templates (with regulator citations such as UIF _indicatori di anomalia_ and FCA/JMLSG) instead of starting from scratch.
- **Curate lists once, apply everywhere**: country-risk sets, keyword packs, and counterparty block/allow lists referenced by key from any rule.
- **Backtest before going live**: dry-run a rule against your stored transactions and compare simulated decisions with the stored baseline; nothing is persisted or billed.
- **Route alerts to your review workflow**: rules can raise alerts that appear in the transaction detail and fire a signed webhook to your systems.
- **Rehearse in the sandbox**: seed a deterministic synthetic transaction book, with a controllable share of suspicious patterns, through the real ingest path.
## Key concepts
Transaction
A payment event (deposit, withdrawal, or transfer) reported by your integration, idempotent on your external ID
Rule
A versioned condition over transaction, list, and identity fields that contributes a probability when it fires, see{' '}
Rules and Templates
Rule template
A catalog entry, jurisdiction-tagged, with regulator citations, you can instantiate into your own rule set with one click
List
A managed set of countries, keywords, or counterparties referenced by rules, see{' '}
Lists
Decision
The synchronous verdict, ALLOW, REVIEW, or BLOCK, derived from the combined{' '}
P(risk) and your thresholds
Alert
A review-queue entry raised by a rule with Raise alert, also delivered via webhook, see{' '}
Settings and Alerts
## Dashboard sections
The **Transaction Monitoring** area of the dashboard is organized in five sections:
Transactions
Every monitored payment event with its status, decision, P(risk), and fired rules, see{' '}
Transactions
Rules
The enabled rule set, the rule builder, per-rule version history, and backtesting
Rule Templates
The jurisdiction-tagged catalog to bootstrap your rule set from
Lists
Country-risk sets, keyword packs, and counterparty block/allow lists referenced by your rules
Settings
Decision thresholds, the timezone driving time-of-day rule fields, and the alert webhook
## Next steps
1. Explore ingested events on the [Transactions](/docs/guides/transaction-monitoring/transactions) page, or seed sample data in the sandbox.
2. Configure [rules or instantiate templates](/docs/guides/transaction-monitoring/rules-and-templates).
3. Curate the [lists](/docs/guides/transaction-monitoring/lists) your rules reference.
4. Tune thresholds and wire up the alert webhook in [Settings and Alerts](/docs/guides/transaction-monitoring/settings-and-alerts).
:::info API reference
The ingest and read endpoints for reporting transactions from your backend are documented separately in the API reference (coming soon).
:::
---
## Lists
**Transaction Monitoring → Lists** holds the managed lists your rules reference: country-risk sets, keyword packs, and counterparty block/allow lists. Rules reference a list by its **key**, so updating a list immediately updates every rule that uses it, no rule edits required.
The Lists page, grouped by kind, system-curated lists ship out of the box and can be overridden.
## List kinds
Counterparty Blocklist
Account identifiers (IBANs, wallet addresses) you never want to transact with
Counterparty Allowlist
Trusted counterparties, usable in rules to suppress noise on known-good flows
Country Risk
Sets of two-letter country codes grouped by risk (matched against origin and counterparty countries)
Keywords
Terms screened against the payment description (for example the SEPA causale)
Each entry can carry an optional **note** with context for reviewers.
## System lists and overrides
Zyphe ships **system-curated lists** (marked with a _System_ badge) that you can reference from rules out of the box. Your organization's own lists extend them, and an org list created with the **same key** as a system list **overrides** it wherever rules reference that key. The system list is marked _Overridden_ and its content stops matching until your override is deleted.
Deleting an org list means rules referencing its key stop matching its entries on the next evaluation.
## How rules use lists
The [rule builder](/docs/guides/transaction-monitoring/rules-and-templates) exposes list matches as condition fields:
- **Counterparty blocklist / allowlist hit**: the transaction's counterparty account appears on a block or allow list.
- **Country-risk hit**: the origin or counterparty country appears in a country-risk list (any list, or a specific one by key).
- **Keyword list hit**: the payment description contains a term from a keyword list.
Rule templates often reference system lists by key, so instantiating a template works immediately, and you can sharpen it later by overriding the list with your own entries.
---
## Rules and Templates
**Transaction Monitoring → Rules** holds the rules evaluated on every ingested transaction. Each fired rule contributes a probability, and the noisy-OR combination of all contributions decides Allow / Review / Block.
The Rules page lists the enabled rule set with each rule's key, version, type, and probabilities.
## Anatomy of a rule
A rule is made of:
- **Rule key**: a stable lowercase slug (`a–z`, `0–9`, `-` and `_`) that identifies the rule across versions.
- **Name and description**: for your reviewers.
- **Condition**: when it matches, the rule fires and contributes its probability to the combined `P(risk)`.
- **Probabilities**: how much a firing contributes, between `0` and `1`.
- **Raise alert**: whether a firing also creates a review-queue alert and notifies your webhook.
### The condition builder
Conditions are built visually as nested **AND / OR groups** of field comparisons (equals, not equals, greater/less than, in a set, contains, is empty / not empty).
The rule builder: name, stable rule key, and the visual condition editor with nested groups.
Fields come from three sources:
Transaction
Amount, currency, direction, payment status, counterparty name / account / country, origin country, channel,
rail, payment description, and derived time fields, hour of day, day of week, outside business hours (driven by
the timezone in Settings)
Lists
Matches against your managed lists, counterparty
blocklist / allowlist hits, country-risk list hits, keyword list hits
Identity
Signals from the verified identity behind the transaction, KYC risk score and account age
### Boolean vs banded rules
There are two rule types:
- **Boolean signal**: the rule fires at a single probability whenever its condition matches. Example: _counterparty is on the blocklist → contribute `0.9`_.
- **Banded metric**: on top of the condition, a numeric field (the **metric**) is banded against **low / medium / high cutoffs**, and each band contributes its own probability. Example: _withdrawal amount banded at 1 000 / 5 000 / 20 000 → contribute `0.1` / `0.3` / `0.7` depending on where the amount lands_. Cutoffs must be ordered (low ≤ med ≤ high), and any subset of them can be provided.
## Versioning
Rules are **append-only**: saving an edit creates a new version and enables it atomically, so the change is live on the very next ingested transaction. The rule page shows the full **version history**: enabling a previous version disables every other version of that key, and disabling a rule stops it from firing on the next ingest while keeping all versions reconstructable.
Because ingested transactions record the exact rule versions that fired, historical decisions remain explainable regardless of how the rule set evolves.
## Backtesting
Before enabling a rule (or a new version of one), you can **backtest** it: a dry-run of the rule, as currently edited, against your stored transactions over a chosen scan window. The backtest reports:
- how the **simulated decisions** compare with the stored baseline (Allow / Review / Block distribution);
- how often the candidate rule **would fire**, per band for banded rules;
- a **sample of fired transactions** with their simulated decision and `P(risk)`.
You can run the candidate rule alone or combined with the currently enabled rules; a candidate sharing an enabled rule's key replaces it for the run. Nothing is persisted or billed, the backtest uses the same validation and evaluation core as live ingestion.
The rule page: Raise alert toggle, the Backtest panel, and the append-only version history below.
## Rule templates
**Transaction Monitoring → Rule Templates** is a jurisdiction-tagged catalog of ready-made rules, with regulator citations (for example UIF _indicatori di anomalia_, FCA/JMLSG) plus universal primitives that apply anywhere.
The template catalog, filterable by jurisdiction, each card shows the condition, type, probabilities, and alert flag.
**Instantiating** a template copies it into your rule set as version 1, enabled, with template provenance recorded. The condition comes from the template; the cutoffs and probabilities are yours to tune afterwards like any other rule.
---
## Settings and Alerts
**Transaction Monitoring → Settings** configures how the combined `P(risk)` turns into a decision, which timezone drives time-of-day rule fields, and where alerts are delivered.
The Settings page: review and block thresholds over P(risk), the timezone, and the alert webhook.
## Decision thresholds
Two percentage thresholds over the combined `P(risk)` split the decision space:
| Range | Decision |
| ------------------------------------ | -------- |
| `P(risk)` below the review threshold | Allow |
| `P(risk)` ≥ review threshold | Review |
| `P(risk)` ≥ block threshold | Block |
Tightening or loosening the thresholds takes effect on the next ingested transaction; already-decided transactions keep their original verdict.
## Timezone
An IANA timezone (for example `Europe/Rome`) drives the derived time fields available in the rule builder, hour of day, day of week, and outside business hours. Set it to the timezone your transaction book actually operates in, so night-time and weekend rules fire when you expect.
## Alerts
A rule saved with **Raise alert** does more than contribute probability when it fires:
- an **alert** is recorded against the transaction, visible in the transaction detail's fired-rules breakdown;
- the alert is delivered to your **webhook**, so you can route it into your own review or case-management workflow.
Alerts carry full provenance, the rule key, the exact rule version, and the probability it contributed, and every raised alert is also written to the immutable audit trail.
## Alert webhook
Configure in Settings:
- **Webhook URL**: the HTTPS endpoint receiving a notification whenever a rule raises an alert. The payload identifies the transaction (including your external ID), the verified identity, the decision, the combined risk probability, and the raised alerts with their rule provenance.
- **Webhook signing secret**: used to sign deliveries so you can verify they come from Zyphe, following the same signature scheme as the platform's other webhooks (see [Webhook signature](/docs/guides/webhook/webhook-signature)). The secret is write-only: it is never displayed after saving, and leaving the field blank keeps the current one.
---
## Transactions
**Transaction Monitoring → Transactions** lists every payment event streamed by your integration, each with the synchronous Allow / Review / Block verdict it received at ingest time.
## The transaction list
Each row shows:
- **External ID**: your transaction identifier (also the idempotency key).
- **Occurred At**: the client event time you reported.
- **Direction**: `Deposit`, `Withdrawal`, or `Transfer`.
- **Amount** and currency.
- **Counterparty**: the other leg of the payment.
- **Status**: the payment lifecycle status (`Pending`, `Settled`, `Failed`, `Canceled`, `Reversed`), updatable by your integration as the payment progresses.
- **Decision**: the monitoring verdict (`Allow`, `Review`, `Block`), or _Not evaluated_ for transactions ingested before any rules were configured.
- **P(risk)**: the combined risk probability behind the decision.
- **Fired Rules**: the rules that contributed to the decision.
The list can be filtered by status and decision, and clicking a row opens the transaction detail.
The Transactions page lists every ingested payment event with its synchronous verdict.
## Transaction detail
The detail page breaks a single transaction down into:
- **Payment**: external ID, direction, channel, rail, description, and the timeline: when it occurred, when it was ingested, and when it was decided.
- **Parties**: the verified identity (and your own user reference), origin country, and the counterparty's name, country, and account identifier.
- **Fired rules**: the heart of the page: each rule that fired, its version, the band it landed in (for banded rules), the probability it contributed, and whether it raised an alert. The header shows the combined risk probability and explains the combination: `P(risk) = 1 − ∏ (1 − pᵢ)`, compared against your review and block thresholds.
The transaction detail shows the payment, the parties, and how each fired rule contributed to the decision.
Because every fired rule is recorded with its exact version, a decision can always be re-explained later, even after the rule has been edited or disabled.
## Seeding sample data (sandbox)
In the sandbox, the **Seed sample data** action generates a deterministic synthetic transaction book through the real ingest path, so it arrives already evaluated against your configured rules. You control:
- the **seed**: same seed, same book, which keeps demos reproducible;
- the number of **transactions** and **identities**;
- the **time window** in days;
- the **suspicious share**: the percentage of transactions drawn as suspicious patterns: structuring bursts, high-risk countries, flagged keywords, night-time movements.
Re-running the same seed replays idempotently, and the outcome reports how many transactions were created, how many replayed, and how many alerts were raised. Seeding is available in the sandbox only, production books are real data.
---
## Document verification(How-it-works)
Document verification confirms that a government-issued identity document is authentic, readable, and acceptable for your flow, and extracts the fields you need for onboarding and compliance.
## What it does
1. Guides the user to capture or upload the document
2. Runs authenticity and quality checks (security features, tampering, copy / screen attacks, score thresholds)
3. Applies your **country** and **document-type** filters
4. Enforces **minimum age** when configured
5. Produces a step result (`PASSED`, `FAILED`, or review statuses) with optional structured `reasons`
## Where it sits in a flow
Document verification is usually the first identity step. Pair it with [liveness](/docs/how-it-works/liveness) so you know a live person matches the document portrait. Other steps (PoA, phone, forms) can follow depending on risk appetite.
```text
Document verification → Liveness → (optional PoA / form / phone) → Result + webhook
```
## Outcomes you should handle
| Outcome | What to do |
| ---------------------- | -------------------------------------------------------------------------------------------------------------- |
| `PASSED` | Continue the flow or accept the identity evidence |
| `FAILED` + `reasons[]` | Map codes to UX (retry, different document, support), [statuses and codes](/docs/reference/statuses-and-codes) |
| Manual review | Keep the user pending until review completes, [manual review](/docs/guides/manual-review) |
## Configure it
Operational settings (thresholds, filters, labels) live in the flow builder:
→ **[Document verification guide](/docs/guides/create-a-flow/document-verification)** · **[Create a flow](/docs/guides/create-a-flow)**
## Related
- [Verification process](/docs/verification-process), public link vs SDK
- [Webhook payload: DV](/docs/guides/webhook/webhook-payload#document-verification-datadv)
- [Reusable credentials](/docs/reusable-credentials-one-click-kyc), reuse a prior verification when eligible
---
## How it works
Short product explainers: what each capability does, where it sits in a flow, and where to configure it. For architecture and data movement, use [Verification process](/docs/verification-process) and [Data flows](/docs/data-flows). For click-by-click setup, use [Guides](/docs/guides/dashboard).
| Capability | Outcome | Guide |
| --------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------- |
| [Document verification](./document-verification.md) | Authenticate and extract identity documents | [Configure](/docs/guides/create-a-flow/document-verification) |
| [Liveness](./liveness.md) | Confirm a live person matches the ID portrait | [Configure](/docs/guides/create-a-flow/liveness) |
| [Proof of address](./proof-of-address.md) | Verify residency evidence | [Configure](/docs/guides/create-a-flow/proof-of-address) |
| [Business verification (KYB)](./kyb.md) | Verify companies, UBOs, and directors | [Configure](/docs/guides/kyb) |
Also in Overview (deeper concepts):
- [AML](/docs/aml)
- [Transaction monitoring](/docs/transaction-monitoring)
- [Reusable credentials](/docs/reusable-credentials-one-click-kyc)
- [Scoring](/docs/guides/scoring) (operational home of the shared scoring engine)
---
## Business verification (KYB)
KYB verifies a **company** rather than an individual: registry data, ownership (UBOs), directors, documents, and screening, with an optional agentic review path for scale.
## What it does
1. Collects business identity and structure (legal entity, addresses, key people)
2. Cross-checks sources and uploaded evidence
3. Tracks **moderation** and **PEP / sanctions** statuses through the case lifecycle
4. Records an audit trail of submissions, document requests, and review actions
5. Can run the [agentic review engine](/docs/guides/kyb/agentic-review-engine) to draft findings under policy guards
## How it differs from KYC
| | KYC | KYB |
| ---------------- | ----------------------------- | ----------------------------------------------- |
| Subject | Natural person | Legal entity + related people |
| Primary evidence | ID document, liveness, PoA | Registry, ownership, corporate docs |
| Typical outcome | Flow result for an individual | Business case with multi-party data |
| Integration | Flow steps + webhooks | Dashboard + API-oriented review (see KYB guide) |
## Statuses to know
Moderation moves through `ACQUIRING` → `PENDING_MODERATION` / `REVIEW_REQUIRED` → `SUCCESS` or `FAILED`. PEP & sanctions use `PENDING`, `IN_PROGRESS`, `REVIEW`, `SUCCESS`, `FAILED`. Details: [Statuses and codes](/docs/reference/statuses-and-codes#kyb-and-aml-statuses).
## Configure and operate
→ **[KYB guide](/docs/guides/kyb)** · **[Agentic review engine](/docs/guides/kyb/agentic-review-engine)**
## Related
- [AML overview](/docs/aml), person and entity screening concepts
- [Scoring](/docs/guides/scoring), organization-defined risk scores
- [Sandbox and go-live](/docs/guides/sandbox-and-go-live)
---
## Liveness
Liveness confirms that a **real person** is present and that their face matches the portrait from [document verification](/docs/how-it-works/document-verification). It reduces spoofing (photos, screens, masks) and is the usual companion step for full identity verification.
## What it does
1. Requires a completed document step that produced a portrait
2. Prompts the user through an active liveness capture
3. Runs anti-spoof and quality checks (brightness, sharpness, occlusion, eyes, …)
4. Compares the live face to the document portrait against your match threshold
5. Optionally supports biometric uniqueness checks (contact support to enable)
## Where it sits in a flow
Always **after** document verification (or another step that supplies a reference portrait). Without that link, face match has nothing trusted to compare against.
## Outcomes you should handle
Failures often surface as DV/liveness-related reason codes such as `LIVENESS_CHECK_FAILED`, `LIVENESS_ANTISPOOF_FAILED`, or `FACE_MATCH_CHECK`. See [Statuses and codes](/docs/reference/statuses-and-codes). Tune the **minimum portrait match percent** if you see too many false rejects vs fraud risk.
## Configure it
→ **[Liveness guide](/docs/guides/create-a-flow/liveness)** · **[Create a flow](/docs/guides/create-a-flow)**
## Related
- [Document verification](/docs/how-it-works/document-verification)
- [Verification process](/docs/verification-process)
- [Scoring](/docs/guides/scoring), combine biometric and other signals into review policy
---
## Proof of address(How-it-works)
Proof of address (PoA) verifies that a user can present acceptable evidence of residency (utility bills, bank statements, and similar documents), with checks on type, recency, name match, and address extraction.
## What it does
1. Collects an allowed address document
2. Validates document type against your configuration
3. Checks name consistency with the verified identity where applicable
4. Enforces recency (documents that are too old fail with `DATE_TOO_OLD`)
5. Returns a step result with optional structured `reason`
## Where it sits in a flow
Usually **after** core identity (document ± liveness), when regulation or risk policy requires residency evidence. It can also run in flows that already know the user from reusable credentials.
## Outcomes you should handle
| Reason (examples) | Meaning |
| --------------------------- | --------------------------------------- |
| `DOCUMENT_TYPE_NOT_ALLOWED` | User must upload a different type |
| `NAME_MISMATCH` | Name does not match identity |
| `DATE_TOO_OLD` | Document outside allowed age |
| `UNRECOGNIZED_ADDRESS` | Address could not be parsed confidently |
Full list: [Statuses and codes: PoA](/docs/reference/statuses-and-codes#proof-of-address-reasons-datapoareason).
## Configure it
→ **[Proof of address guide](/docs/guides/create-a-flow/proof-of-address)** · **[Create a flow](/docs/guides/create-a-flow)**
## Related
- [Geolocation step](/docs/guides/create-a-flow/geolocation), location-based signals when configured
- [Webhook payload: PoA](/docs/guides/webhook/webhook-payload)
---
## Statuses and codes
This page is the **authoritative vocabulary** for statuses, webhook events, and structured reasons you will see in the dashboard, webhooks, and API. Prefer these enums over free-text `errorMessage` when branching application logic.
Payload field layout lives in [Webhook Payload](/docs/guides/webhook/webhook-payload). Failure handling is in [Failure and Review Events](/docs/guides/webhook/failure-payload).
---
## Three layers of status
A single webhook can carry different statuses at different layers. Do not treat them as synonyms.
| Layer | Field | Scope |
| ------------------ | ------------------------------------------------------------------------------- | --------------------------------------- |
| **Delivery event** | top-level `event` | Why this webhook was sent |
| **Flow / request** | `flowStatus` (webhook) · `status` on a flow result / verification request (API) | Overall outcome of the verification run |
| **Step result** | `data..status` | Outcome of one step (DV, PoA, form, …) |
Example: a DV step can be `FAILED` with `reasons: ["DOCUMENT_EXPIRED"]` while you still handle a later step, or the whole flow can move to `REQUIRES_MANUAL_REVIEW` after a `REVIEW` event.
---
## Webhook `event` values
### Verification results
| Value | Meaning | Typical next action |
| -------------- | ---------------------------------------------- | --------------------------------------------------- |
| `COMPLETED` | The step or flow completed successfully | Trust `data` / `flowStatus`; provision the user |
| `FAILED` | The step or flow failed automatically | Read structured reasons; allow retry or reject |
| `REVIEW` | Human review is required | **Do not reject yet**. Wait for a later final event |
| `REJECTED` | Explicit rejection (often after manual review) | Treat as declined |
| `NOTIFICATION` | Lifecycle notification (not a step result) | Route on `eventData.type` |
### AML updates
| Value | Meaning |
| ----------- | ------------------------------------------------------------ |
| `PRODUCED` | Initial AML screening result produced |
| `REFRESHED` | Ongoing monitoring refreshed matches |
| `MODERATED` | An AML match was moderated (approved / rejected / escalated) |
### Notification `eventData.type`
Used only when `event` is `NOTIFICATION`:
| Type | Meaning |
| ----------------------- | --------------------------------------------------------------- |
| `DOCUMENT_EXPIRED` | A verified identity document has expired |
| `KYC_RESULT_EXPIRED` | A KYC / flow result has passed its retention or validity window |
| `FLOW_RISK_REEVALUATED` | Risk scores were recomputed (for example after tag changes) |
---
## Flow and verification-request status
`flowStatus` on result webhooks and `status` on verification requests / flow results use the same overall lifecycle:
| Status | Meaning |
| ------------------------ | ---------------------------------- |
| `PENDING` | Created; user has not finished |
| `QUEUED` | Waiting to be processed |
| `PROCESSING` | Automated checks in progress |
| `COMPLETED` | Finished successfully |
| `FAILED` | Finished with an automatic failure |
| `CANCELLED` | Cancelled before completion |
| `REQUIRES_MANUAL_REVIEW` | Waiting on an operator decision |
| `REQUIRES_ADMIN_REVIEW` | Waiting on an admin-level decision |
| `REJECTED` | Explicitly rejected |
---
## Step result status (`data..status`)
Individual step results (document verification, PoA, form, and related types) use:
| Status | Meaning |
| ------------------------ | ----------------------------------------- |
| `PASSED` | Step checks passed |
| `FAILED` | Step checks failed |
| `PARTIAL` | Incomplete or partially successful result |
| `REQUIRES_MANUAL_REVIEW` | Operator must decide |
| `REQUIRES_ADMIN_REVIEW` | Admin must decide |
:::warning
Webhook examples may show intermediate labels such as `REVIEW` on a nested result while the flow is under review. Treat **`REVIEW` at the event / flow layer** as “keep pending,” and use the final `COMPLETED` / `FAILED` / `REJECTED` event as the decision to act on in your product.
:::
---
## Document verification reasons (`data.dv.reasons[]`)
Empty when no reason is reported. Values come from the API `DvReason` enum.
### Document quality and authenticity
| Code | Typical meaning |
| -------------------------------- | ----------------------------------------------------- |
| `WRONG_DOCUMENT_TYPE` | Submitted type does not match what the step allows |
| `DOCUMENT_TYPE_NOT_DEFINED` | Type could not be determined |
| `DOCUMENT_TYPE_CHECK` | Type validation failed |
| `DOCUMENT_NOT_FOUND` | No usable document in the capture |
| `DOCUMENT_SCORE_BELOW_THRESHOLD` | Authenticity / quality score below configured minimum |
| `SECURITY_CHECK_FAILED` | Built-in security features failed checks |
| `DOCUMENT_ALTERATION_DETECTED` | Signs of alteration |
| `DOCUMENT_TAMPERING_DETECTED` | Signs of tampering |
| `FAKE_DOCUMENT_DETECTED` | Classified as fake |
| `SCREEN_REPLAY_ATTACK_DETECTED` | Screen / replay presentation attack |
| `PAPER_COPY_DETECTED` | Paper copy rather than original |
| `BLACK_AND_WHITE_COPY` | B/W copy of a color document |
| `ELECTRONIC_DEVICE` | Capture from another screen/device |
| `SECURITY_TEXT` | Security text check failed |
| `IMAGE_PATTERNS` | Image-pattern check failed |
| `PHOTO_EMBEDDINGS` | Portrait embedding consistency failed |
### Country, age, and field rules
| Code | Typical meaning |
| --------------------------------- | ------------------------------------------ |
| `COUNTRY_FILTER` | Country not allowed by whitelist/blacklist |
| `DOCUMENT_COUNTRY_MISMATCH` | Country does not match expected value |
| `DOCUMENT_COUNTRY_MISSING` | Country could not be read |
| `DATE_OF_EXPIRY_NOT_VALID` | Expiry invalid or document expired |
| `DATE_OF_EXPIRY_NOT_READABLE` | Expiry unreadable |
| `DATE_OF_EXPIRY_FIELD_MISSING` | Expiry field missing |
| `DATE_OF_BIRTH_BELOW_MINIMUM_AGE` | Below the flow’s minimum age |
| `REQUIRED_FIELDS_MISMATCH` | Required extracted fields inconsistent |
### Face, liveness, and uniqueness
| Code | Typical meaning |
| --------------------------------------- | ------------------------------------------------------ |
| `FACE_MATCH_CHECK` | Face match against the document failed |
| `FACE_MATCH_CHECK_ONE_CLICK` | Face match failed in a one-click / reuse path |
| `LIVENESS_CHECK_FAILED` | Liveness overall failed |
| `LIVENESS_ANTISPOOF_FAILED` | Anti-spoofing failed |
| `LIVENESS_BRIGHTNESS_FAILED` | Lighting / brightness out of range |
| `LIVENESS_EYES_CLOSED_FAILED` | Eyes closed |
| `LIVENESS_WEARING_SUNGLASSES_FAILED` | Sunglasses detected |
| `LIVENESS_FACE_OCCLUDED_FAILED` | Face occluded |
| `LIVENESS_SHARPNESS_FAILED` | Image not sharp enough |
| `LIVENESS_FACE_CONSISTENCY_FAILED` | Face consistency across frames failed |
| `UNIQUENESS_CHECK` | Biometric uniqueness / duplicate identity check failed |
| `DOCUMENT_ALREADY_USED_BY_ANOTHER_USER` | Document already bound to another identity |
| `DOCUMENT_FINGERPRINT_COMPUTATION` | Document fingerprinting failed |
### Other
| Code | Typical meaning |
| ---------------- | --------------------------------------------------------- |
| `OVERALL_STATUS` | Aggregate step status failed without a more specific code |
| `UNKNOWN` | Unclassified reason |
---
## Proof of address reasons (`data.poa.reason`)
| Code | Typical meaning |
| --------------------------- | ----------------------------------------- |
| `DOCUMENT_TYPE_NOT_ALLOWED` | Type not accepted for PoA |
| `NAME_MISMATCH` | Name does not match the verified identity |
| `DATE_TOO_OLD` | Document older than allowed window |
| `INVALID_DOCUMENT` | Document not valid for PoA |
| `UNRECOGNIZED_ADDRESS` | Address could not be recognized |
| `UNKNOWN` | Unclassified reason |
Other step types (`form`, `phone`, `wallet`, `kyb`, `geolocation`, `spid`) may expose optional `reason` / `errorMessage` strings. Prefer structured fields when present; use `errorMessage` for diagnostics only.
---
## KYB and AML statuses
### KYB moderation (`ModerationResultStatus`)
| Status | Meaning |
| -------------------- | ----------------------------------- |
| `ACQUIRING` | Collecting company / ownership data |
| `PENDING_MODERATION` | Waiting for review |
| `REVIEW_REQUIRED` | Needs human review |
| `SUCCESS` | Moderated successfully |
| `FAILED` | Failed |
### PEP & sanctions (KYB)
| Status | Meaning |
| ------------- | --------------------- |
| `PENDING` | Not started |
| `IN_PROGRESS` | Screening in progress |
| `REVIEW` | Match requires review |
| `SUCCESS` | Cleared |
| `FAILED` | Failed |
### AML moderation decisions
When an AML match is moderated: `approved`, `rejected`, or `escalated`.
See [AML](/docs/aml) and [KYB](/docs/guides/kyb) for operational workflows.
---
## Transaction monitoring decisions
Transaction monitoring returns a synchronous verdict derived from combined rule risk:
| Decision | Meaning |
| -------- | ---------------------------------------------------- |
| `ALLOW` | Below review threshold, proceed |
| `REVIEW` | At or above review threshold, hold for investigation |
| `BLOCK` | At or above block threshold, do not proceed |
See [Transaction Monitoring](/docs/transaction-monitoring) for the Allow / Review / Block model and [TM guides](/docs/guides/transaction-monitoring) for rules and thresholds.
---
## Manual review
| Concept | Value / event |
| ------------------ | ---------------------------------------------------------------- |
| Flagged for review | Result status `REQUIRES_MANUAL_REVIEW` · webhook `event: REVIEW` |
| Operator decision | Manual review result: `approved`, `rejected`, or `failed` |
| After decision | Follow-up webhook with `COMPLETED`, `FAILED`, or `REJECTED` |
Do not mark the end user as rejected on `REVIEW` alone. See [Manual review](/docs/guides/manual-review).
---
## Related
- [Webhook structure](/docs/guides/webhook/webhook-structure), envelopes and delivery
- [Webhook payload](/docs/guides/webhook/webhook-payload), field-level examples
- [OpenAPI reference](/docs/openapi/zyphe-sdk), generated schemas (`DvReason`, `ResultStatus`, `VerificationRequestStatus`, …)
---
## Advanced topics
---
## Proof of KYC
### Introduction
#### Purpose
This document is a technical guide for implementing **verifiable Proof of KYC** (Know Your Customer), usable both **on-chain** by smart contracts and **off-chain** by APIs, relayers, and traditional services.
It outlines several methods for proving that a wallet, identifier, or credential holder has successfully completed a KYC process. Smart contract developers can consume the on-chain variants directly; backend developers, identity wallets, and compliance services can verify the same artifacts off-chain. Both consumers can build custom business logic on top of the verified identity data while preserving compliance and user privacy.
#### Overview
The **Proof of KYC** solution provides a privacy-preserving way to verify a user's KYC status without exposing the underlying identity attributes, neither on a public ledger nor to off-chain verifiers that don't need them.
Zyphe offers five distinct methods, each with different privacy properties, integration complexity, and verification surface:
| Method | Verifiable on-chain | Verifiable off-chain |
| ---------------------------------------------------------- | :-----------------: | :------------------: |
| KYC Soulbound Token issuance | ✓ | - |
| Signed KYC Mint Voucher | ✓ | ✓ |
| Ethereum Attestation Issuance | ✓ | ✓ |
| W3C Verifiable Credentials | ✓\* | ✓ |
| Zero-Knowledge Proofs derived from a Verifiable Credential | ✓ | ✓ |
\* W3C VCs are typically verified off-chain, but signed presentations can also be checked on-chain via verifier contracts.
#### Key Components
**Verifiable Credentials (VCs)**
Verifiable Credentials represent identity information issued by Zyphe after the KYC verification process. These credentials are cryptographically signed and attest that the user has undergone a KYC process. Each VC is associated with metadata, such as the issuer, date of issuance, and the type of verification performed (e.g., identity verification, proof of address, etc.).
**Attestations**
An attestation is a cryptographic statement that Zyphe has validated a user's identity. It can be persisted **on-chain** (as a smart-contract event or attestation registry entry) or held **off-chain** as a signed message, JWT, or JSON-LD document. In both cases, the verification surface is public while the underlying KYC data remains off-chain and minimized.
**Cryptographic Signatures**
Cryptographic signatures verify the integrity and authenticity of verifiable credentials and attestations. When Zyphe issues a credential, it signs it with its private key. **Smart contracts** can verify these signatures on-chain using the corresponding Zyphe public key (registered in an issuer registry), and **off-chain verifiers** (backends, wallets, relayers) can do the same with standard cryptographic libraries. Only artifacts from trusted issuers are accepted.
**KYC Tokens**
To ensure easy interaction with decentralized applications (dApps) and marketplaces, users who pass the KYC process can be issued a **non-transferable token** (e.g., a soulbound token). This token acts as a proof-of-KYC that is stored on the blockchain. While the token itself does not reveal any sensitive details, dApps can verify its existence to ensure the user has been KYC-verified by Zyphe.
**Smart Contracts**
For on-chain consumption, the Proof of KYC solution is backed by smart contracts that validate signatures, attestations, and zk-proofs. These contracts define the logic for:
- Issuing attestations or soulbound tokens upon verification.
- Validating cryptographic proofs when requested by another contract or a dApp.
- Managing the lifecycle of issued credentials, including issuance and revocation.
Off-chain integrations replace these contracts with equivalent verifier libraries running inside backends, wallets, or compliance services, using the same issuer keys and revocation state.
**Signed KYC Mint Voucher**
It’s a cryptographically signed message that a protocol can utilize within its smart contract. It provides smart contract developers with full flexibility to implement custom business logic on their side, such as issuing tokens, adding wallet addresses to a whitelist, or other actions based on the KYC verification.
### KYC Soulbound Token issuance
In the **KYC Soulbound Token Issuance** solution, the user first connects and authenticates their wallet. Upon successful completion of the KYC process, our backend service issues a **non-transferable Soulbound Token** directly to the authenticated wallet. This token acts as an immutable on-chain representation that the KYC process has been completed, allowing decentralized applications (dApps) to verify the user’s KYC status without exposing any sensitive information.
### Flow
```mermaid
sequenceDiagram
participant Dapp
box Zyphe
participant FE as Frontend
participant BE as Backend
participant KYC as KYC Module
end
participant BC as Blockchain
Dapp->>FE: redirect
Note over FE: connect wallet
FE->>BE: initiate SIWE login
BE->>FE: send challenge
Note over FE: sign message
FE->>BE: send signed message
BE->>FE: start user session
BE->>KYC: initiate kyc session
KYC->>BE: send challenge
BE->>FE: send kyc session
Note over FE: initiate kyc provider iframe
FE->>KYC: perform kyc required actions
KYC->>BE: kyc result webhook
BE->>BC: mint token
BC->>FE: send soulbound token
BE->>FE: send kyc result
FE->>Dapp: confirmation
```
#### KYC Soulbound Token issuanceSigned KYC Mint Voucher
In the **Signed KYC Mint Voucher** solution, the user first connects and authenticates their wallet. After successfully completing the KYC process, our backend service generates and signs a message that attests to the completion of the KYC. This signed message, similar to a JWT but using EVM-compatible data types, can be consumed by a smart contract on-chain. This approach provides developers with the flexibility to implement their own business logic, such as issuing tokens, whitelisting addresses, or other custom workflows.
### Zero-Knowledge Proofs from a Verifiable Credential
In the **zk-Proof from VC** solution, Zyphe issues a W3C Verifiable Credential signed under a ZKP-friendly scheme (BBS+ over BLS12-381, or CL signatures for AnonCreds-style flows). The holder then derives a **zero-knowledge proof** that attests to specific predicates over the credential, without revealing the underlying attributes, the credential identifier, or the issuer signature itself.
The same proof artifact is **verifiable both on-chain and off-chain** under identical cryptographic guarantees. This makes it the most flexible primitive in the Zyphe stack: a single VC can serve a Solidity verifier contract on an EVM L1, an aggregator inside an ERC-4337 paymaster, and an off-chain compliance API, with no re-issuance and no PII leakage.
#### Flow
The lifecycle splits into two phases: **issuance** of the VC after KYC (one-time, off-chain), and **presentation** of a derived zk-proof against a verifier (per-request, on- or off-chain).
```mermaid
sequenceDiagram
participant Dapp
box Zyphe
participant FE as Holder(Frontend / Wallet)
participant BE as Backend(Issuer)
participant KYC as KYC Module
end
participant BC as Verifier Contract
Note over Dapp,BC: Phase 1: Credential issuance (one-time)
Dapp->>FE: redirect with required predicates + contextId
Note over FE: connect wallet,generate link_secret
FE->>BE: SIWE login + commit(link_secret)
BE->>FE: challenge
Note over FE: sign challenge
FE->>BE: signed message
BE->>FE: start session
BE->>KYC: initiate kyc session
KYC->>BE: session challenge
BE->>FE: kyc session
FE->>KYC: perform kyc actions
KYC->>BE: kyc result webhook
Note over BE: issue VC (BBS+ over attributes,bound to link_secret)
BE->>FE: deliver VC + revocation handle
Note over Dapp,BC: Phase 2: Proof presentation (per request)
Dapp->>FE: request proof(predicates, contextId)
Note over FE: derive zk-proof locally:verify BBS+, prove predicates,bind wallet, compute nullifier
alt On-chain verification
FE->>BC: submit proof + public inputs
Note over BC: verify proof, check IssuerRegistry,expiry, nullifier, revocation
BC-->>Dapp: emit event / state update
else Off-chain verification
FE->>Dapp: proof + public inputs
Note over Dapp: verify proof, check issuer pubkey,track nullifier
end
```
#### Cryptographic primitives
- **Issuer signature**: BBS+ over BLS12-381. Multi-message signatures allow the holder to selectively disclose or blind individual credential attributes.
- **Proof system**: Groth16, PLONK, or STARK circuits encode (a) the BBS verification relation, (b) the requested predicates (range proofs, set membership / non-membership, hash equality), and (c) holder binding.
- **Holder binding**: a `link_secret` bound at issuance is committed inside every proof and signed against the wallet challenge, preventing credential pooling, rental, or transfer.
- **Nullifiers**: a deterministic `nullifier = H(link_secret, contextId)` is published with each proof. Verifiers track nullifiers per-context to prevent replay while preserving cross-context unlinkability.
- **Revocation**: handled out-of-band via `BitstringStatusList` (W3C) or a Merkle / RSA accumulator, with the current root mirrored on-chain for trustless checks.
#### What can be proved
The same VC satisfies many predicates without re-issuance:
- `kyc_level >= L2`: KYC tier threshold
- `age >= 18` (or any threshold): range proof over date-of-birth
- `country ∈ allowlist` / `country ∉ sanctioned_set`: set membership / non-membership
- `risk_score < threshold`: numeric bound when AML scoring is bound into the credential
- `wallet == msg.sender`: binding the proof to the calling EOA / smart account
- `issuer == zyphe_did ∧ not_expired(now)`: issuance authority and validity window
#### On-chain verification
A verifier contract is generated from the circuit and consumes a calldata-friendly proof. A typical EVM entrypoint:
```solidity
function verifyKyc(
uint256[8] calldata proof,
uint256[] calldata publicInputs, // [issuerKeyId, predicateHash, nullifier, contextId, expiry]
bytes32 nullifier
) external returns (bool);
```
The contract enforces:
1. `Verifier.verify(proof, publicInputs) == true`
2. `publicInputs.issuerKeyId` is registered in the on-chain `IssuerRegistry`
3. `publicInputs.expiry > block.timestamp`
4. `usedNullifiers[contextId][nullifier] == false`, then marks it used
5. (Optional) credential is unrevoked against the mirrored revocation root
Gas cost is dominated by the pairing check: roughly 230k gas for a Groth16 verifier on BN254 via the `ecPairing` precompile (EIP-197), and single-digit-millisecond verification off-chain.
#### Off-chain verification
The same proof verifies under `snarkjs`, `arkworks`, `rapidsnark`, or `risc0`, enabling hybrid flows:
- **Rollups / L2**: cheap on-chain verification, or batched verification at the sequencer.
- **Account abstraction (ERC-4337)**: validate the proof inside `validateUserOp` (paymaster or signature aggregator) to gate UserOps without leaking PII.
- **Off-chain APIs**: a backend issues a session token after verifying the proof, with no need to ever hold the underlying VC.
#### Privacy and threat model
- **Zero-knowledge**: only the predicate result and the declared public inputs are revealed; no attribute, signature, or credential identifier leaks.
- **Unlinkability**: across distinct `contextId` values, two presentations of the same VC cannot be correlated by the verifier(s).
- **Per-context Sybil resistance**: nullifiers prevent reuse within a context while preserving anonymity across contexts.
- **Issuer compromise**: a leaked issuer key enables forgery of new credentials going forward, but does not retroactively deanonymize existing presentations. Keys are rotated via the `IssuerRegistry`.
- **Trusted setup**: Groth16 requires a per-circuit ceremony; PLONK requires a universal setup; STARKs are transparent. Zyphe favors universal or transparent setups to avoid per-circuit ceremonies.
### AML compliance proof
AML (Anti-Money Laundering) frameworks function fundamentally as a blacklist: they identify and restrict users or entities associated with high-risk activities. This contrasts with KYC, which confirms an individual's identity but does not assess their transactional history or risk indicators. A user who has successfully completed KYC verification may still be flagged on AML lists if linked to suspicious activities. KYC proves that an individual is known and verified; AML provides a separate compliance layer focused on monitoring and preventing illicit activity, even for KYC-verified users.
- **AML Screening (off-chain)**\
The compliance process begins off-chain, where a user's wallet address (and, where applicable, identity attributes) is checked against up-to-date AML lists maintained by trusted providers. These lists flag addresses associated with money laundering, fraud, or sanctions. If the address is clear, a cryptographic attestation is created to certify that the wallet has passed AML checks.
- **Issuing an AML compliance proof**\
Once the wallet passes screening, Zyphe issues a **cryptographic attestation** stating that the address is not blacklisted. This attestation can take the form of a signed message, a JWT, a W3C VC, or a token with an AML status attribute, any of which can be consumed by on-chain or off-chain verifiers.
- **On-chain verification**\
The attestation can be persisted on-chain as a smart-contract event or registry entry. Other contracts check its validity without needing access to the full AML database. It acts as a public "AML passport" while the underlying screening data stays private.
- **Off-chain verification**\
The same attestation is verifiable off-chain by APIs, custodians, exchanges, or compliance backends using standard signature-verification libraries against Zyphe's published issuer keys.
- **Access control**\
When a user interacts with a dApp or service, the verifier checks the attestation's signature against the trusted AML issuer registry. Based on this proof, the verifier can permit or deny access, apply transaction limits, or trigger additional compliance measures.
_More coming soon…_
---
## Overview
Anti-Money Laundering (AML) screening verifies people and organizations against sanctions, PEP, watchlists, and adverse media to prevent the use of financial services for illicit purposes. Zyphe integrates AML screening with its decentralized **Know Your Customer (KYC)** solution to deliver end‑to‑end compliance from onboarding through ongoing monitoring. We screen identities across multiple lists and compute a risk score to help you make fast, defensible decisions.
Zyphe’s approach enables **privacy‑preserving compliance**. Instead of centrally storing sensitive personal data, Zyphe keeps user data decentralized and under the user’s control. Only the minimum necessary attributes or cryptographic credentials are shared with the screening engine and third‑party data sources (e.g., sanctions, PEP, adverse media providers). This achieves **data minimization** while maintaining high screening quality. Screening events and outcomes can be logged with strong cryptography and, where applicable, blockchain technology to provide an immutable audit trail for regulators and partners.
---
## When screening runs
- Onboarding (KYC/KYB)
- Periodic re‑screening and list refreshes
- Event‑driven triggers (profile updates, behavior changes)
Purpose
Screens identities for sanctions, PEP, watchlists, and adverse media
Timing
During onboarding and on a recurring basis; event‑driven re‑screening
Scope
Individuals and organizations
Process
Multi‑list queries, fuzzy matching, disambiguation; consolidated risk scoring with configurable rules
Key Benefits
Reduces regulatory risk; blocks high‑risk users; improves review efficiency
---
## Risk Scoring
For each subject screened against sanctions, PEP, and adverse media lists, Zyphe consolidates the matched signals into a single, normalized **risk score** and a categorical **risk level**. The scoring engine is fully configurable per organization, so each tenant can align the assessment to its own risk appetite, regulatory perimeter, and internal policies.
:::tip Beyond AML
The AML risk score is one input to Zyphe's generalized scoring engine: organizations can define additional custom scores fed by form answers, workflow actions, and manual review. See the [Scoring](/docs/guides/scoring) section.
:::
## Output
Every AML check produces, for each matched entity, a structured result that includes:
Risk Score
Normalized integer percentage from 0 to 100
Risk Level
Categorical band derived from the score: Low, Medium, High, or Critical
Sources Count
Number of independent data sources that contributed to the match
### Risk levels
The AML risk score uses the platform-wide score levels defined in [Scoring](/docs/guides/scoring#score-levels), the same `Low / Medium / High / Critical` bands and thresholds apply here. The level is what most reviewers act on; the percentage is what flow builder conditions and audit trails reference. The **typical handling** column below is AML-specific guidance.
| Score range | Risk level | Typical handling |
| ----------- | ---------- | --------------------------------------------- |
| `0`, `25` | Low | Auto‑approve, log for audit |
| `26`, `50` | Medium | Auto‑approve with monitoring, or light review |
| `51`, `75` | High | Manual review by a compliance analyst |
| `76`, `100` | Critical | Block or escalate; requires senior review |
In the dashboard, risk levels are rendered as color‑coded badges so reviewers can triage cases at a glance.
## How the score is computed
The score combines two families of signals: **tag weights** (the inherent severity of each matched topic) and **contextual factors** (multipliers that adjust the score based on jurisdiction, position, and relationship signals associated with the match).
```mermaid
flowchart LR
A["Matched entity(topics, country, position, relationships)"] --> B["Tag weightsseverity per topic"]
A --> C["Contextual factorsjurisdiction · position · relationship"]
B --> D["Base score"]
C --> E["Adjustment"]
D --> F["Adjusted score"]
E --> F
F --> G["Normalize to 0–100"]
G --> H["Risk levelLow · Medium · High · Critical"]
```
In short:
1. Each topic the entity matches (e.g., `sanction`, `role.pep`, `crime.fraud`) contributes a weighted severity to a base score.
2. The base score is adjusted by the contextual signals attached to the match, for example, a high‑risk jurisdiction or a senior government position increases the score multiplicatively.
3. The result is normalized against the worst‑case configuration to produce a stable percentage in `[0, 100]`.
The full mathematical specification is part of Zyphe's internal algorithm documentation and is available on request for compliance review.
## Configurable tag weights
A **tag weight** assigns a severity (a positive number) to a specific risk topic. The higher the weight, the more strongly a match on that topic pushes the score upward.
Zyphe ships with a curated set of global default weights covering the standard AML taxonomy, sanctions, terrorism, war crimes, fraud, PEP categories, regulatory warnings, and more. The defaults are designed to produce sensible scores out of the box for most organizations.
Each organization can:
- **Override** any default weight to make a topic count more or less heavily.
- **Add custom tags** that are unique to the organization (for internal classifications that aren't part of the global AML taxonomy).
- **Activate or deactivate** any tag without deleting it, which is useful when temporarily excluding a signal from scoring.
Tag weights are managed from the **Risk Tags** page in the dashboard, under your organization's settings. The page lists every effective tag for the organization and indicates, for each row, whether it is a global default, a local override, or a custom entry.
Review global AML tags, custom tags, weights, and active status from the Risk Tags page.Create an organization-specific override to adjust the weight of a global AML tag.
## Configurable contextual factors
A **contextual factor** is a multiplier applied on top of the base score when a specific contextual signal is present in the match. There are three types:
Jurisdiction
Country‑level multipliers based on ISO‑3166 codes, applied when the entity's country, nationality, or country of birth matches a configured factor
Position
Multipliers tied to roles or occupancies, such as senior government officials, security agency leadership, or judicial figures
Relationship
Multipliers tied to relationship topics, such as a person being linked to a sanctioned entity or sitting on the board of a shell corporation
As with tag weights, Zyphe provides global defaults covering the most common jurisdictions, positions, and relationship classes. Organizations can override any default factor or disable factors that don't apply to their use case. Contextual factors are managed from the **Risk Contextual Factors** page.
Review contextual factor multipliers and their global defaults from the dashboard.Create an organization-specific override to tune contextual factor multipliers.
:::info Defaults vs. overrides
Both tag weights and contextual factors follow the same precedence model: organization‑level configuration takes priority over global defaults. When you create an override, the global default is preserved and shown alongside the override so you always have a reference point.
:::
## Assigning custom risk tags from a flow
Beyond the screening provider's output, your verification flows can attach **custom risk tags** to a specific result, for example to mark a subject who failed a manual document check as carrying additional internal risk. Custom tags assigned this way are folded into the next risk score computation for that flow result, using the weights configured for the organization.
This is configured with the **Assign Risk Tags** action node inside the flow builder. The action takes one or more custom tags previously defined on the Risk Tags page and persists them on the flow result.
## Using risk scores in flow builder conditions
The flow builder exposes the AML risk score as a condition input. You can branch a flow based on the percentage value with operators such as greater‑than, less‑than, or equals, which lets you encode policies like "send to manual review if score ≥ 60" or "auto‑reject if score ≥ 85" directly in the flow definition, without writing custom code.
## Where risk scores appear in the dashboard
Risk scores are surfaced wherever AML results are reviewed:
- **Flow result detail**: the AML section lists every matched entity with its risk level, match score, and topic tags, plus a moderation status (Under Review, Approved, Rejected, Escalated).
- **Entity detail view**: clicking through an entity shows the risk score percentage, risk level badge, the originating sources, the field‑level controls, and the audit trail of moderation actions.
- **Case management**: risk levels drive sorting and filtering of flow results so reviewers can prioritize the highest‑risk cases first.
## Programmatic access
Risk scores, levels, and the underlying matched signals are available through the Zyphe REST API as part of the AML result payload. They are intended for integration with internal compliance systems, BI tooling, and case management workflows.
:::info Webhook scope
AML webhooks include an update summary under `data.aml`: the update kind, moderation status, subject type, PEP and sanctions flags, and optional risk-score percentage. Use the Dashboard or AML result API when you need the full screening result and provider matches. See [Webhook Payload](/docs/guides/webhook/webhook-payload#aml-dataaml).
:::
All inputs to the score (matched topics, jurisdictions, positions, relationships, and any custom tags assigned via the flow builder) are persisted alongside the result so the score can be re‑explained and audited at any time.
---
## Choosing an integration method
Zyphe supports three main ways to put verification in front of a user. All of them share the same **flows**, **results**, and **webhooks**: you choose based on engineering effort, UX control, and how you want to operate the product.
## At a glance
| | **Public link** | **SDK** | **Agent / MCP** |
| -------------------------- | ----------------------------------------- | ----------------------------------- | ------------------------- |
| **Setup time** | Minutes | Hours | Minutes with an agent |
| **Engineering** | None–low | Low–medium | Low (AI-assisted) |
| **User stays in your app** | No (hosted UI) | Yes (embed or redirect you control) | Depends on what you build |
| **Best for** | Pilots, email/SMS invites, no-code launch | Production web or mobile UX | Automating setup and ops |
```
Do you need a working verification today with almost no code?
├─ YES → Public link
└─ NO → Do you need a native experience in your app?
├─ YES → SDK (browser, Node session creation, React Native)
└─ NO → Are you configuring the platform with an AI assistant?
├─ YES → MCP / agent tools
└─ Prefer API-driven ops → Node SDK + webhooks + dashboard
```
---
## Public link
Each flow has a shareable URL. The user completes verification on Zyphe's hosted UI; your backend is notified via webhook and results appear in the dashboard.
**Choose public link when:**
- You need to start verifying users immediately
- Engineering capacity is limited
- You are running a pilot or proof of concept
- You distribute invites by email, SMS, or partner portals
**How it works (high level):**
1. Create a [flow](/docs/guides/create-a-flow) in the dashboard
2. Share the public link (or generate a session link from the server when you need per-user context)
3. Receive a [signed webhook](/docs/guides/webhook) when the result is ready
4. Review the [flow result](/docs/guides/manual-review) (or automate with scoring)
→ [Verification process: public web link](/docs/verification-process) · [Sharing a flow](/docs/guides/create-a-flow/sharing)
---
## SDK
Use the SDK when you want control over session creation, branding in-context, or a native mobile experience.
| SDK | Role |
| ---------------- | --------------------------------------------------------------------------- |
| **Node** | Create verification sessions server-side; generate links or tokens securely |
| **Browser** | Embed the verification UI in your web app (iframe) |
| **React Native** | Present verification in a mobile WebView |
**Choose SDK when:**
- Users should stay in your product experience
- You need server-side session creation with your own user IDs and metadata
- You are building production web or mobile onboarding
Typical production pattern:
1. Backend creates a session with the [Node SDK](/docs/guides/sdk/node) and an [API key](/docs/guides/api-keys)
2. Frontend opens the session with the [browser](/docs/guides/sdk/browser) or [React Native](/docs/guides/sdk/react-native) SDK
3. Backend consumes [webhooks](/docs/guides/webhook) and optionally polls or fetches result details via API
→ [SDK overview](/docs/guides/sdk) · [Verification process: SDK](/docs/verification-process)
---
## Agent / MCP
Zyphe ships a [Model Context Protocol (MCP)](/docs/guides/model-context-protocol-mcp) server so compatible assistants (Claude Desktop, Claude Code, Cursor, and others) can create and manage flows, forms, and API keys through natural language, authenticated with a bot token.
**Choose MCP when:**
- You want to scaffold or iterate on flows quickly
- Operators or developers already work with AI coding agents
- You are automating repetitive dashboard setup
MCP complements public link and SDK; it does not replace user-facing verification. Ship the integration with a link or SDK, and use the agent for configuration and maintenance.
→ [MCP guide](/docs/guides/model-context-protocol-mcp) · [LLM-friendly docs](pathname:///llms.txt)
---
## What every path still needs
Regardless of integration method:
1. A **sandbox** organization to test without production data. See [Quickstart](/docs/quickstart)
2. A **flow** that defines the steps the user completes
3. A way to **receive results**: webhooks are recommended for production
4. Clear **roles** so reviewers and developers only see the PII they need, [Manage users](/docs/guides/manage-users) · [PII access control](/docs/pii-access-control)
---
## Recommended next step
- New to Zyphe → **[Quickstart](/docs/quickstart)** (public link or SDK, end to end)
- Already know you need embed → **[Browser SDK](/docs/guides/sdk/browser)** or **[React Native](/docs/guides/sdk/react-native)**
- Automating setup → **[MCP](/docs/guides/model-context-protocol-mcp)**
---
## Authority Check
```mermaid
sequenceDiagram
autonumber
participant AUT as Authority
participant ZB as Zyphe Backend
participant COMP as Company
participant DCS as Decentralized Storage
AUT->>+COMP: Legal request to access Data
COMP->>+AUT: Provide references to data
AUT->>+DCS: legal request to access data by reference
DCS-->>+DCS: check if authority has a legitimate access
DCS-->>+AUT: provide data
```
Zyphe's commitment to balancing user privacy with legal compliance. The steps ensure that when authorities legally request access to data, the process is stringent, verifying the legitimacy of the request and providing data only when necessary and appropriate. This demonstrates Zyphe's robust approach to handling sensitive user data within the decentralized storage model.
1. **Legal Request to Access Data**: An authority initiates the process by issuing a legal request to a company to access specific data. This is the formal beginning of a procedure that is likely regulated by laws and policies to protect user privacy.
2. **Giving the list of Hashes**: The company responds to the authority by providing a list of hashes. Hashes are typically unique alphanumeric strings that correspond to specific pieces of data, used to maintain the privacy and security of the data being referred to.
3. **Legal request to check data of those hashes**: The authority, with the list of hashes, makes a legal request to check the actual data corresponding to those hashes. This step ensures that the authority is targeting specific data points for its inquiry.
4. **Check if the Authority has a legitimate request**: Before any data is provided, the decentralized storage system checks if the authority's request is legitimate. This might involve verifying the legal basis of the request, such as a court order or a subpoena.
5. **Provide Data**: If the request is verified as legitimate, the corresponding data is retrieved from the decentralized storage and provided to the authority.
---
## Creation of the User Vault
```mermaid
sequenceDiagram
actor User
autonumber
User->>Zyphe Backend: Authenticate
Zyphe Backend->>Zyphe Backend: Create User Vault
Zyphe Backend-->>User: User is asked to grant permission to store personal data
User->>Zyphe Backend: Grant permission
Zyphe Backend->>Decentralized Storage: Shard encrypted data among different nodes with TH + P encryption
Zyphe Backend->>Zyphe Backend: Create UUID
```
1. **Authenticate**: This is the initial step where the user signs up for services with Zyphe, initiating their journey into a decentralized identity ecosystem.
2. **Create User Vault**: Once registered, Zyphe's backend system creates a Personal Online Datastore (Vault) for the user. A Vault is a secure storage space where personal data is kept.
3. **Permission Request**: In alignment with privacy-by-design principles, the user is then prompted to grant Zyphe permission to store personal data. This is a crucial step emphasizing user control and consent in the data management process.
4. **Grant Permission**: After the user grants permission, the process of safeguarding their data can proceed. This permission ensures that Zyphe acts within the legal and ethical boundaries set by data protection regulations.
5. **Data Sharding**: Zyphe then takes the user's WebID, a unique identifier for the user in the decentralized web, and shards it using Threshold + Polynomial Encryption (TH + P Encryption). Sharding is the process of breaking the data into pieces and distributing it across multiple nodes (servers), and this specific encryption method adds layers of security and redundancy.
6. **Store User WebID and Create UUID**: The final step is storing the encrypted and sharded WebID in the decentralized storage. Alongside this, a Universal Unique Identifier (UUID) is created for the user, likely to further ensure the uniqueness of the user's identity within the system.
This dataflow represents a user-centric model of identity management where privacy and security are paramount. By decentralizing storage and employing advanced encryption methods, Zyphe ensures that the user's identity and data are protected against centralized points of failure and unauthorized access.
```mermaid
sequenceDiagram
autonumber
Zyphe Backend->>Encryption System: Send data for encryption
Encryption System->>Encryption System: Perform TH + P encryption
Encryption System->>User Vault: Send encrypted data
User Vault->> User Vault: Fragmentation
User Vault->> Decentralized Storage: Upload into the distributed decentralized storage
```
7. **Send Data for Encryption**: Our story begins with data embarking from the Zyphe backend. This is where the raw user data, perhaps provided during registration or subsequent activities, is prepared to be sent for encryption.
8. **Perform TH + P Encryption**: The next chapter sees the data undergoing our robust Threshold Homomorphic + Polynomial encryption. This sophisticated encryption technique ensures that data, while still usable for computations, is securely encrypted and can be split into multiple pieces.
9. **Send Encrypted Data**: Once encrypted, the data is then dispatched to the user's User Vault. This secure transmission ensures that the encryption integrity remains intact.
10. **Fragmentation**: Within the safe confines of the User Vault, the data undergoes fragmentation. This step is akin to dividing a secret into several parts, with each part being meaningless on its own. It’s a measure that significantly enhances data security.
11. **Upload and Distributed into the Decentralized Storage**: In the final leg of its journey, the data fragments are uploaded and distributed across the decentralized storage infrastructure. This not only adds another layer of security but also ensures that data retrieval requires assembling the distributed fragments, making unauthorized access practically impossible.
By now, the data has transformed from its original state into a secure, encrypted, and fragmented form, residing within a decentralized framework. This guarantees the highest level of privacy and security, allowing users to engage with digital services with peace of mind, knowing their data is secure within Zyphe's system.
---
## Data Deletion
```mermaid
sequenceDiagram
autonumber
actor User
participant ZB as Zyphe Backend
participant COMP as Company
participant DCS as Decentralized Storage
User->>+ZB: Request to delete data
ZB-->>+ZB: check data retention requirements for all connected companies
ZB-->>+ZB: revoke access to companies if the retention period has passed
ZB-->>+ZB: mark data for deletion
ZB-->>+User: confirmation
loop check if there is data to delete
alt There is data to delete
ZB->>+DCS: delete data
ZB-->>+User: notification
else
ZB-->>+ZB: check again
end
end
```
It's important to note that this process indicates Toggggle's adherence to legal compliance by creating temporary storage for data if needed for legal reasons before fully deleting it from the system.
1. **User submits deletion request**: Via their account settings or UI.
2. **Evaluate retention obligations**: The backend checks all third-party recipients and internal policies to determine which copies must be retained and for how long.
3. **Revoke unnecessary access**: The backend immediately disable access for any party that no longer has a legal or contractual need to hold the data.
4. **Flag data for deletion**: The backend marks the user’s data “pending deletion” once every remaining retention obligation has expired.
5. **Acknowledge receipt**: The backend sends the user an immediate confirmation that their request is in progress.
**Daily compliance check**:
- Every 24 hours, re-evaluate retention obligations.
- Revoke any newly ineligible access.
- Delete all data whose retention period has lapsed.
**Finalize and notify**:
- After data is irreversibly deleted, send the user a “deletion complete” notification (e.g. by email).
---
## Data Flows
This section explains how Zyphe creates, verifies, shares, accesses, and deletes identity data. Each page documents one flow, including the actors involved and the controls applied to the data.
## Overview
Zyphe's data flows are designed around **decentralized identity**, **user sovereignty**, and **privacy by design**. Users control when their data is shared, while organizations receive only the access required for a specific purpose.
## How the flows fit together
```mermaid
flowchart LR
start([User starts]) --> vault[Create encrypted user vault]
vault --> verify[Verify identity]
verify --> credential[Issue verified credential]
credential --> reuse[Reuse credential with another organization]
credential --> passport[Share through KYC Passport]
passport --> access[Authorized organization accesses data]
authority[Authority submits legal request] --> check[Validate authority and scope]
check --> access
deletion[User requests deletion] --> retention{Retention required?}
retention -->|Yes| restrict[Restrict access until retention expires]
retention -->|No| remove[Revoke access and delete eligible data]
restrict --> remove
```
The main lifecycle runs from vault creation to verification and controlled reuse. Authority access and deletion are separate, policy-driven paths that apply only when their legal conditions are met.
## Core Data Flows
### [User Vault Creation](./creation-of-the-user-vault.md)
The foundational flow that establishes a user's secure digital identity. This process creates a personal online datastore (vault) where user data is encrypted, sharded, and distributed across decentralized storage using advanced cryptographic techniques.
**Key Features:**
- Threshold + Polynomial (TH + P) encryption
- Data sharding across multiple nodes
- User consent and permission management
- UUID generation for unique identification
### [User Verification Flow](./user-verification-flow.md)
The standard KYC (Know Your Customer) verification process that validates user identity and issues verifiable credentials. This flow supports documents from 190+ countries and can include separate document verification and liveness detection steps.
**Key Features:**
- Multi-country document verification (190+ countries)
- Active liveness detection (separate step)
- Portrait matching between document and live selfie
- Verifiable Credential (VC) issuance
- Secure data encryption and storage
### [Reusable Credentials / One-Click KYC](../reusable-credentials-one-click-kyc/index.md)
A flow that enables users to reuse previously verified credentials across different companies, eliminating the need for repeated verification processes.
**Key Features:**
- Credential reuse across companies
- Access grant system
- Metadata-based eligibility checking
- Passwordless authentication support
### [KYC Passport](./kyc-passport.md)
A flow that allows organizations to securely share verified KYC data with partner organizations without requiring users to repeat verification.
**Key Features:**
- Partner organization invitations
- User consent management for data sharing
- Cryptographic access grants for partners
- Flow-specific data sharing
- Invite lifecycle management (accept/reject/revoke)
### [Organization Data Access Flow](./organization-data-access.md)
The flow that enables an organization to access user data it has been granted, ensuring proper authorization and user consent. For the operational dashboard product, see the [Dashboard](/docs/guides/dashboard) guide.
**Key Features:**
- Company authentication
- User permission verification
- Secure data retrieval
- Access control management
### [Authority Check](./authority-check.md)
A compliance-focused flow that handles legal requests for data access from authorities while maintaining user privacy and ensuring legitimate access.
**Key Features:**
- Legal request verification
- Hash-based data referencing
- Authority legitimacy checking
- Regulatory compliance
### [Data Deletion](./data-deletion.md)
The "right to be forgotten" flow that ensures users can request complete deletion of their data while respecting legal retention requirements.
**Key Features:**
- User-initiated deletion requests
- Retention obligation evaluation
- Access revocation
- Automated compliance checking
## Data Flow Principles
All Zyphe data flows adhere to these core principles:
1. **User Sovereignty**: Users maintain full control over their data and identity
2. **Privacy by Design**: Data protection is built into every flow from the ground up
3. **Decentralized Storage**: Data is distributed across multiple nodes for enhanced security
4. **Cryptographic Security**: Advanced encryption ensures data remains secure
5. **Regulatory Compliance**: All flows comply with relevant data protection regulations
6. **Transparency**: Clear visibility into how data is processed and shared
## Technical Architecture
The same security layers support every data flow:
```mermaid
flowchart TB
actors[Users, organizations, and authorities]
policy[Consent, permissions, and legal scope]
grants[Recipient-specific access grants]
crypto[Encryption and proxy re-encryption]
data[Vault data, credentials, and metadata]
storage[Distributed storage nodes]
actors --> policy
policy --> grants
grants --> crypto
crypto --> data
data --> storage
```
The architecture includes:
- **Decentralized Storage**: Distributed data storage across multiple nodes
- **Threshold + Polynomial Encryption**: Advanced cryptographic techniques
- **Proxy Re-Encryption (PRE)**: User-to-organization sharing without ever exposing plaintext to Zyphe (see [Data Sharing and Encryption](../decentralized-identity/data-sharing-and-encryption.md))
- **Verifiable Credentials**: Standards-compliant digital credentials
- **Access Grants**: Re-encryption keys that authorize a specific recipient to decrypt a specific document
- **Metadata Management**: Efficient data discovery without compromising privacy
## Getting Started
Read the flows in this order for a complete introduction:
1. [User Vault Creation](./creation-of-the-user-vault.md) establishes the storage and consent foundation.
2. [User Verification Flow](./user-verification-flow.md) verifies identity and issues a credential.
3. [Reusable Credentials / One-Click KYC](../reusable-credentials-one-click-kyc/index.md) shows how a verified credential can be reused.
4. [KYC Passport](./kyc-passport.md) explains controlled sharing with partner organizations.
---
## KYC Passport(Data-flows)
The KYC Passport lets an organization securely share verified KYC data with partner organizations,
so users don't repeat verification across an ecosystem, while keeping the sharing under the user's
consent and control.
This page covers the **conceptual architecture**: how access grants are generated, the rules that
govern them, and the sequence flows behind partner data access. For the operational side, inviting
partners, accepting invitations, managing consent, screenshots, and FAQs, see the
[KYC Passport guide](/docs/guides/kyc-passport).
## Overview
A main organization invites partner organizations to access KYC results from specific flows. The
user consents once, during verification, to sharing with the named partners. This creates a trusted
ecosystem where verified identity data is shared efficiently without re-verification.
## Key benefits
- **Seamless partner integration**: share KYC results with multiple partners without repeat verification.
- **User-centric consent**: users explicitly consent to the exact list of partners.
- **Efficiency**: removes redundant verification across partner organizations.
- **Secure access control**: cryptographic access grants ensure only authorized partners can decrypt data.
## Consent rules (architectural invariants)
Consent is the precondition for every partner access grant:
- **No consent = no access.** If the user does not consent, no partner grants are created, only the main organization receives a grant.
- **Invite timing matters.** Partners invited _after_ a user completes verification do not retroactively receive access to that user's data.
- **Consent is flow-specific.** It binds to a specific flow and the list of partners invited at verification time.
## Access grant generation
```mermaid
sequenceDiagram
actor User
participant BE as Backend
participant MO as Main Org
participant IA as Invite A(Accepted)
participant IB as Invite B(Pending)
participant IC as Invite C(Rejected)
User->>+BE: Start verification
BE->>+User: Show privacy consentPartners: A, B, C
User->>+BE: Accept and continue
Note over BE,MO: User completes verification
BE-->>+MO: Create grant for Main Org
BE-->>+IA: Create grant for Invite A(if already accepted)
BE-->>+IB: Create grant for Invite B(will activate when accepted)
Note over IC: No grant for Invite C(rejected)
BE->>+User: Verification complete
```
### Access grant rules
1. **Main organization**: always receives a grant when the user completes verification with consent.
2. **Accepted partner invites**: receive a grant immediately.
3. **Pending partner invites**: a grant is created and becomes active when the partner accepts.
4. **Rejected partner invites**: no grant is created.
5. **No consent**: no partner grants are created (only the main organization).
## Partner invitation lifecycle
### Accepting an invite (existing organization)
```mermaid
sequenceDiagram
autonumber
participant ORGA as Partner Org
participant BE as Backend
participant DB as Database
ORGA->>+BE: GET /invites
BE->>+ORGA: Return pending invites
ORGA->>+BE: POST /accept-invite(inviteId, code)
BE-->>+BE: Verify invite code
BE-->>+DB: Update invite status to ACCEPTED
BE-->>+DB: Link invite to organization
BE->>+ORGA: Invite accepted successfully
Note over ORGA: Organization can now accessshared flow results
```
### Accepting an invite (new organization)
```mermaid
sequenceDiagram
autonumber
participant ORGB as New Partner Org
participant BE as Backend
participant DB as Database
ORGB->>+BE: POST /register(with invite details)
BE-->>+DB: Create new organization
BE-->>+DB: Update invite status to ACCEPTED
BE-->>+DB: Link invite to new organization
BE->>+ORGB: Registration and invite accepted
Note over ORGB: New organization can nowaccess shared flow results
```
## Partner data access
### Main organization data access
```mermaid
sequenceDiagram
autonumber
participant MO as Main Org
participant BE as Backend
participant ACL as Access Control
participant DB as Database
participant DCS as Decentralized Storage
MO->>+BE: GET /{orgId}/flow-results
BE->>+ACL: Check organization permissions
ACL->>+BE: Permissions granted
BE->>+DB: Get flow results
DB->>+BE: Return flow result metadata
BE-->>+BE: Retrieve access grant(orgId, identityId, documentType)
BE->>+DCS: Fetch encrypted data
DCS->>+BE: Return user data
BE->>+MO: Return complete data
```
### Partner organization data access
```mermaid
sequenceDiagram
autonumber
participant SUB as Partner Org
participant BE as Backend
participant ACL as Access Control
participant DB as Database
participant DCS as Decentralized Storage
SUB->>+BE: GET /{orgId}/shared-flow-results
BE->>+ACL: Check invite-based permissions
ACL->>+BE: Invite has valid permissions
BE->>+DB: Get flow results for this invite
DB->>+BE: Return accessible flow results
BE-->>+BE: Retrieve access grant(inviteId, flowId, identityId, documentType)
BE->>+DCS: Fetch encrypted data
DCS->>+BE: Return user data
BE->>+SUB: Return complete data
```
:::info Access grant differences
- **Main organization**: grants establish a connection between the organization and the verified document.
- **Partner organizations**: grants associate the invite with the verified document, linking it to the invitee organization on acceptance.
This ensures partners can only access data from the specific flow they were invited to, and only for users who completed verification after the invite was created (with proper consent).
:::
## Security considerations
- **Access grants are proxy re-encryption keys** that transform ciphertext from the user's key to the partner's key (see [Data Sharing and Encryption](../decentralized-identity/data-sharing-and-encryption.md)). Zyphe acts as the proxy: it performs the transformation but never sees plaintext or holds either private key.
- **Data stays encrypted** in decentralized storage under the user's key. Partners receive re-encrypted ciphertext that only their private key can decrypt; a full breach of Zyphe yields only encrypted data and re-encryption keys.
- **Grants are auditable and revocable.** Revoking deletes the re-encryption key, ending future transformations for that pair. Where data-retention obligations apply, access remains until those obligations are met.
- **Invites are authenticated** with a unique `inviteId` and a one-time password (OTP) sent by email, over encrypted HTTPS.
- **Consent is verified** before any grant is created or activated, and is linked to the grant via flow consents.
## Related
- **[KYC Passport guide](/docs/guides/kyc-passport)**: inviting and managing partners, accepting invitations, consent operations, and FAQs.
- **[Reusable Credentials / One-Click KYC](/docs/reusable-credentials-one-click-kyc)**: the related pattern for reusing a credential with a single company.
---
## Organization Data Access Flow
This is the architectural sequence by which an organization reads user data it has been granted
access to. For the operational dashboard product (navigation, reviewing results, and day-to-day
use), see the [Dashboard](/docs/guides/dashboard) guide.
```mermaid
sequenceDiagram
autonumber
participant CD as Company Dashboard
participant ZB as Zyphe Backend
participant DCS as Decentralized Storage
CD->>+ZB: Authenticate
ZB->>+DCS: request user data
DCS-->>+DCS: check if company have access
DCS->>+CD: return data
```
1. **Authenticate**: A company representative logs into the company dashboard. This is the entry point to access user data and likely involves authentication measures to ensure security.
2. **Request user data**: Once logged in, the company's dashboard makes a request to Zyphe's backend system to retrieve specific user data. This request is generated after the user engages with the company and implies that there is some need for the company to access user information stored by Zyphe.
3. **Check if the user has been granted access to the company**: Before data is retrieved, Zyphe's backend system checks whether the user has granted the company permission to access their data. This step is crucial for complying with privacy regulations and ensuring that user data is not accessed without consent.
4. **Return data**: If the user has been granted access, the requested data is retrieved from decentralized storage and returned to the company dashboard. The company can then use this data for the purpose it was requested, such as account management, customer service, or other business functions.
---
## User Verification Flow
```mermaid
sequenceDiagram
autonumber
actor User
participant ZF as Zyphe frontend
participant ZB as Zyphe backend
participant DCS as Decentralized Storage
User->>+ZF: submit data for KYC
ZF->>+ZB: send data for verification
ZB-->>+ZB: perform KYC verification
ZB-->>+ZB: create User Vault
ZB->>+DCS: Encrypt and upload data
ZB-->>+ZB: Issue VC
ZB->>+DCS: Ecrypt and upload VC
```
1. **Perform KYC**: The user begins by submitting their personal data required for KYC. This typically involves two steps: first, taking a picture of their identity document for document verification, and then performing a liveness check to prove they are a real person who matches the document. This is a standard compliance process to verify the identity of the users (documents from 190+ countries are supported).
2. **Perform KYC**: Zyphe takes the submitted data and performs the necessary checks to verify the user’s identity.
3. **Create User Vault**: Once the verification is complete, a user Vault is generated
4. **Encrypt and upload user data**: Zyphe's backend then encrypts the user's data for security.
5. **Issue an interoperable VC**: After the data is securely encrypted, a Verifiable Credential (VC) is issued. VCs are digital certificates that use cryptography to provide a secure and tamper-evident way for a claim to be represented and verified. In our case, the VC is a hash that can be minted in every chain if needed.
6. **Encrypt and upload VC**: The verifiable credential is then encrypted and stored securely in the user vault, along with the personal information that has been used to generate it.
---
## Data Sharing and Encryption
Zyphe shares data between users and organizations using **Proxy Re-Encryption (PRE)**. The user always encrypts data with their own key. When they choose to share with an organization, a **re-encryption key** lets a proxy (Zyphe) transform the user's ciphertext into ciphertext that the organization can decrypt, without the proxy ever seeing the plaintext or learning either party's private key.
This page explains how the scheme works in Zyphe, the two key-management models we offer, and what an attacker would actually obtain in the worst-case breach scenario.
## How Proxy Re-Encryption Works in Zyphe
```mermaid
sequenceDiagram
autonumber
actor User
participant Z as Zyphe (Proxy)
participant DCS as Decentralized Storage
participant Org as Organization
User->>User: Encrypt data withuser public key
User->>DCS: Store ciphertext (encrypted under user key)
User->>User: Generate re-encryption keyuserKey → orgKey
User->>Z: Upload re-encryption key
Org->>Z: Request shared data
Z->>DCS: Fetch user-encrypted ciphertext
Z->>Z: Apply re-encryption key(transform ciphertext)
Z->>Org: Deliver org-encrypted ciphertext
Org->>Org: Decrypt withorg private key
```
Three properties make this safe:
1. **Plaintext never reaches Zyphe.** The proxy only ever handles ciphertext and a transformation key.
2. **The re-encryption key is one-way and pair-specific.** It only converts ciphertext from `userKey → orgKey`. It cannot decrypt anything on its own and cannot be inverted to recover either private key.
3. **Sharing is consent-driven.** A re-encryption key only exists for an `(identity, document, organization)` pair after the user has explicitly granted access. Revoking access removes the key, ending the proxy's ability to transform new requests.
This is the cryptographic mechanism behind every "access grant" referenced in the [Reusable Credentials / One-Click KYC](../reusable-credentials-one-click-kyc/index.md) and [KYC Passport](../data-flows/kyc-passport.md) flows.
## Key Management: Two Models
The user's private key is the only key that can encrypt data into the vault and produce re-encryption keys. Zyphe supports two ways to manage it, with different trade-offs between user control and convenience.
### BYOK with Passkey (Advanced Users)
Bring-your-own-key mode binds the user's private key material to a **FIDO2/WebAuthn passkey** on the user's device. The key is derived client-side using the WebAuthn PRF extension and never leaves the user's authenticator.
- **Custody:** the user's device (Secure Enclave, TPM, hardware key) holds the secret. Zyphe has no copy at any point.
- **Re-encryption keys:** generated locally on the user's device when sharing is granted, then uploaded to Zyphe.
- **Recovery:** governed entirely by the passkey backup story (platform sync, hardware-key replication, etc.). Losing all passkey copies means losing access; Zyphe cannot recover the vault.
- **Best for:** users and organizations that require provable cryptographic isolation from Zyphe.
See [Passkey Authentication](../guides/dashboard/passkey-authentication.md) for the registration and authentication flow.
### Managed Key with No-Log Paradigm (Default)
For users who prefer a traditional flow, Zyphe generates and operates the key on the user's behalf under a strict no-log paradigm.
- **Generation:** the key is generated inside an isolated execution context, used to perform the requested operation (encrypting a vault entry, producing a re-encryption key), and discarded.
- **No-log guarantee:** the key is never persisted to disk, never written to logs, never included in telemetry, and never exposed to other internal services. Only ephemeral, in-memory use is permitted.
- **User unlock:** the user authenticates with their account credentials (passphrase, Vault Access Key, or other supported method) before any key operation runs.
- **Best for:** users who want decentralized, end-to-end encrypted storage without managing cryptographic material themselves.
Both models produce the same on-the-wire artefact stored at Zyphe: a re-encryption key. The difference is **where the user's private key lives**.
## What an Attacker Would Get From a Breach
The threat model is explicit: assume Zyphe's infrastructure is fully compromised. What can the attacker actually do with what they find?
| Asset stored at Zyphe | What it is | What an attacker can do with it |
| --------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Encrypted vault data | Ciphertext encrypted under the user's public key, sharded across decentralized storage | Nothing. Without the user's private key, the ciphertext is opaque. |
| Re-encryption keys | One-way transformation keys for specific `(user → organization)` pairs | Nothing on their own. They transform ciphertext between two public keys but cannot decrypt anything and cannot be inverted to recover either private key. |
| User private keys (BYOK) | Not present | N/A (never uploaded). |
| User private keys (Managed) | Not persisted | N/A (generated ephemerally and discarded after use under the no-log paradigm). |
To actually decrypt a user's data, an attacker would need the **organization's private key** (held by the organization) **and** the corresponding ciphertext **and** the re-encryption key. Even then they would only obtain data the user had already explicitly consented to share with that organization.
A breach of Zyphe alone yields encrypted data and re-encryption keys. Neither, separately or together, is sufficient to decrypt anything.
## Why This Matters
- **No central honeypot.** Zyphe physically cannot decrypt user data: there is no master key, no "break-glass" decryption path, and no plaintext cache.
- **Granular, revocable sharing.** Each shared document corresponds to a distinct re-encryption key. Revocation simply deletes that key; no further requests can be transformed.
- **Compliance posture.** Because the proxy never sees plaintext, the system aligns with data-minimization principles in GDPR and similar frameworks by construction, not by policy.
- **Operational simplicity for organizations.** Recipients use standard public-key decryption against ciphertext they receive: no need to integrate with a key escrow or run a decryption service against Zyphe's storage.
---
## Definitions
## User Vault (User Vault)
A User Vault is where personal information is securely stored. It contains the user’s verified and encrypted data, such as documents collected during the document verification or generated verifiable credentials. The User Vault is distributed as the data it contains is fragmented and uploaded across several storage nodes within the network.
## Threshold Encryption (TH Encryption)
Threshold encryption is a cryptographic technique that allows a group of participants to jointly decrypt a message, without revealing the message to any individual participant. The decryption requires cooperation and agreement from a specified minimum number of participants, known as the threshold.
## Proxy ReEncryption or Transform encryption (PRE)
Proxy re-encryption (PRE) is a cryptographic technique that allows a third party (the proxy) to transform ciphertext encrypted under one public key into ciphertext encrypted under another public key, without learning the underlying plaintext. This enables secure data sharing where the original data owner doesn't need to be online or share their private key with recipients.
In Zyphe, PRE is the mechanism behind every access grant: the user encrypts vault data under their own key, generates a re-encryption key for the recipient organization, and uploads only that re-encryption key to Zyphe. See [Data Sharing and Encryption](./data-sharing-and-encryption.md) for the full flow and key-management options.
## Re-encryption Key
A re-encryption key is a one-way, pair-specific cryptographic key that lets the proxy transform ciphertext encrypted under the user's public key into ciphertext encrypted under a recipient's public key. It cannot decrypt data on its own and cannot be inverted to recover either party's private key. Revoking access in Zyphe deletes the re-encryption key, preventing any future transformation for that pair.
## Web Identifier (WebID)
WebID (Web Identifier) is a standard way of representing a person or organization on the web, using a unique URI (Uniform Resource Identifier) that serves as a persistent identifier for the entity. The WebID specification defines a decentralized and distributed identity system that allows users to create, manage, and share their own identity online, while also being able to control the use of their personal data.
## Decentralized Identifiers (DID)
DIDs (Decentralized Identifiers) are a new type of globally unique identifier that enables verifiable digital identity without reliance on a centralized authority. They are self-owned, persistent, and cryptographically verifiable, linking subjects (people, organizations, things) to their decentralized key management systems and other identity-related data.
## Universally Unique Identifier (UUID)
A UUID is a 128-bit identifier that is designed to be globally unique, meaning that no two UUIDs are expected to be the same. It is used in computer systems and software applications as a way to uniquely identify entities such as files, devices, users, and transactions.
## Decentralised Storage Secret Management (DSSM)
Secret management systems typically provide a centralized way to securely store, manage, and distribute secrets. They may include features such as encryption, access control, auditing, and automatic rotation of secrets to enhance security. These systems are often integrated with other security tools and processes to provide a comprehensive security solution.
## Know Your Customer (KYC)
KYC stands for "Know Your Customer," which refers to the process of verifying the identity of a customer or client as a part of due diligence and anti-money laundering procedures. KYC is a regulatory requirement in many industries, particularly in banking, finance, and e-commerce, where there is a risk of fraud, money laundering, or terrorist financing.
## Verifiable credentials (VC)
A veriviable credential is a digital record that contains information about a person's identity, qualifications, certifications, or other relevant attributes. It is designed to be a secure, tamper-evident way to share personal information with others, it is based on decentralized identity principles and is designed to be shared directly between individuals, organizations, and other entities.
## Selective disclosure (SD)
Selective disclosure allows you to reveal only specific parts of a verifiable credential while maintaining its verifiability without relying on a trusted third party.
## Verifiable presentation (VP)
In SSI, a Verifiable Presentation is a collection of one or more Verifiable Credentials presented by a holder to a verifier, proving certain claims.
## Zero Knowlege Proofs (ZKP)
Zero-Knowledge Proofs (ZKPs) are a cryptographic method where one party (the prover) can convince another party (the verifier) that a statement is true, without revealing any information beyond the truth of the statement itself.
---
## What is Decentralization for Zyphe
Zyphe applies decentralization as a core business principle, it’s not just a technical feature. By removing single points of failure and control, we ensure that compliance-driven verification processes remain secure, scalable, and future-proof. Our architecture distributes both infrastructure and data ownership across independent nodes and end-users, creating a network that operates autonomously while meeting the highest standards of privacy, reliability, and regulatory resilience. This approach allows enterprises to onboard customers globally without the operational and legal risks associated with centralized data models.
## Two Key Points of Decentralization
Zyphe is decentralized in two ways:
### Geographical Decentralization
Through **geographical decentralization**, Zyphe achieves global infrastructure redundancy and continuity. Each storage node functions independently and is hosted across multiple providers and jurisdictions, ensuring that no single organization, including Zyphe, has control over the network. User data is encrypted, sharded, and distributed across nodes, so even if 80% of the network goes offline, the system remains operational and compliant. This distributed setup protects against downtime, data breaches, and vendor lock-in, providing enterprises with unmatched reliability and cross-border data resilience.
### Ownership Decentralization
The **ownership** of personal information is decentralized. Zyphe returns control of personal information to users while maintaining enterprise-grade compliance. Once verified, user data moves into a personal decentralized vault, removing Zyphe entirely from data custody. Users can reuse verified credentials across multiple platforms instantly, reducing friction, verification costs, and regulatory exposure for businesses. This structure transforms KYC into a reusable, consent-based process, enabling companies to meet compliance obligations efficiently, decease frauds while aligning with global privacy regulations like GDPR and future-ready standards for digital identity.
---
## Decentralized Identity
Zyphe's is dedicated to revolutionizing how personal identity is handled in the digital space, ensuring that control, privacy, and autonomy remain in the hands of our users.
**In this documentation, we provide a comprehensive overview of Zyphe's systems and protocols designed to empower you:**
- **The Evolution of Identity Management**: Understand the progression from traditional Siloed Identities to the innovative Decentralized Identities we champion today.
- **Decentralized Storage Creation**: Discover the intricacies of how we generate and protect your Personal Online Datastore (POD) for ultimate data sovereignty.
- **Dataflow Processes**: Dive into our various dataflow diagrams which detail how your data is managed, from creation, through KYC procedures, to eventual deletion if desired.
- **Legal and Compliance Adherence**: Learn about our commitment to legal compliance and the mechanisms in place for authority checks and data retention.
This guide aims to demystify the complexities of data management, providing you with clarity and confidence in how your digital identity is curated and conserved. Zyphe isn't just a service; it's a declaration of your right to privacy and self-determination in an increasingly connected world.
Thank you for entrusting Zyphe with your digital identity. Together, we're setting the new standard for privacy and user empowerment.
---
## Types of Storages
**Siloed Identities**\
This is a legacy system where identities are scattered across various companies. The drawbacks are clear: users juggle multiple credentials, and companies bear the high cost of managing data infrastructures. Plus, the centralized approach to data management leads to significant security vulnerabilities. **Siloed Identities**. In this archaic model, each company holds a separate fragment of the user's identity. This fragmentation leads to a cumbersome experience for users, who must remember a plethora of usernames and passwords, a true hassle in our digital world. Companies aren't spared either; they face the daunting task of maintaining secure, yet costly data infrastructures. Centralized data storage compounds the problem, posing a high risk of catastrophic data breaches.
\
**Federated Identities**\
This was an improvement, offering a more connected experience. However, user control remained limited, and companies often exploited user data without clear incentives to protect it. The reliance on central databases continued to be a security concern, and the scope for users to selectively disclose their information was minimal.\
As we evolved into **Federated Identities**, we began to see identities linked across services, offering a glimmer of integration. But this system is far from perfect. Users still lack substantial control, and the user experience is dictated by corporate strategies focused on maximizing data extraction rather than protecting user privacy. Moreover, these federated systems create large honey pots of data, enticing targets for malicious actors, which further amplifies security concerns.
**Decentralized Identities**
Here at Zyphe, we're enabling a paradigm where users exercise full control over their identity. The need for corporate access to user data now requires explicit user authorization. Our decentralized approach to data storage minimizes security risks and corporate liabilities. Moreover, it supports full selective disclosure, empowering users like never before. **Decentralized identities** shift the paradigm from large, vulnerable data caches to distributed data nodes, each securely encrypted and autonomously controlled. This reduces the risk of mass data breaches and places the power of selective disclosure firmly in your hands. With Zyphe, you’re not just a user; you’re an individual with a fortified, self-sovereign identity.
---
## Why Decentralized PII Storage
## Why a Distributed Approach is Safer Than Centralized Solutions
In handling sensitive personal identifiable information (PII), **decentralization** offers a fundamentally safer and more privacy-preserving approach than traditional centralized storage. Unlike a centralized system (where one database or authority holds all data and control), a decentralized system distributes control, decision-making, and data storage across many independent nodes. This means no single entity has complete control and no single point of failure, vastly improving resilience, security, and user privacy Below, we explain how **Zyphe’s decentralized architecture** works and why it is _better and safer_ for storing PII.
### Understanding Decentralization and Its Security Benefits
In a decentralized data model, information is **encrypted and fragmented across multiple nodes** rather than kept in one central repository. Each node in the network holds only a piece of the data, and **no single node can access the entire dataset in usable form**. This structure provides several key security advantages:
- **No Single Point of Failure:** A breach of one node does not compromise all data. By distributing encrypted data pieces across many servers, the system _eliminates_ the catastrophic risk of a central database breach. (In contrast, a centralized database is an obvious target: if attackers penetrate it, they can steal _everything_ at once.)
- **Reduced Attack Surface:** Decentralization dramatically shrinks the attack surface available to hackers. Because data shards are spread out, an attacker would have to breach _dozens_ of nodes (and break strong encryption) to gather meaningful information. This makes successful hacks exponentially [more difficult](https://www.zyphe.com/faqs). In fact, analyses show decentralized storage can be \~**96% more resistant to data breaches** compared to traditional centralized data storage.
- **Resilience and Uptime:** With no central server dependency, the system is inherently more fault-tolerant. Even if some nodes go offline or are attacked, the overall network continues to function, preventing downtime and data loss. This resilience ensures continuous availability of identity verification services globally.
In short, decentralization means there is _no single vault to crack_: an attacker cannot simply “hit the jackpot” by hacking one server or database. Any malicious attempt faces a fragmented, encrypted wall of data distributed across the network, a far safer design for PII storage.
### Geographical Decentralization with Zyphe: Near-Impenetrable Data Security
**Zyphe’s platform implements geographical decentralization** as a core strategy to secure PII. Once a user’s identity data is collected and verified, it is immediately encrypted with strong cryptography (AES-256-GCM and other methods). After encryption, Zyphe **splits the data into many small shards** and distributes these shards across a network of nodes in the user’s region. For example, a user's encrypted data might be divided among _hundreds_ of nodes located near that user (to optimize performance and comply with local data residency laws). To reconstruct the original data, a minimum threshold of shards (for instance, 29 out of 100) must be reassembled, providing redundancy while ensuring that no single shard is useful on its own.
**Why is this decentralized sharding approach so secure?** Consider the following scenarios and how Zyphe’s design addresses them:
- **Breach of Zyphe’s Infrastructure:** Even if attackers breached Zyphe’s own systems, they would gain **no usable data**. Zyphe has _no access_ to plaintext PII after it’s encrypted and shard-distributed. The company doesn’t store complete datasets or any “master key,” meaning there’s no central trove of data to steal. This virtually **eliminates the risk of a platform-wide breach** exposing user information, a stark contrast to traditional providers whose databases can be raided in one go.
- **Compromise of a Storage Node:** If one of the decentralized storage nodes is hacked or fails, the attacker obtains only an **encrypted fragment** of a user’s data (for example, just 1% of a document, which is meaningless on its own). **At least 29** independent shards would be required to reconstruct anything intelligible, and each shard is indecipherable without the decryption keys. Thus, a single server breach is effectively useless to an attacker. This _fragmentation and distribution strategy significantly reduces the risk of unauthorized access or data breaches_
- **Company-Wide or Server Cluster Breach:** In a worst-case event where multiple nodes or even a large portion of the network is compromised, the **threshold encryption** ensures that unless the attackers control a large majority of nodes (far beyond a feasible scope) _and_ break the encryption, the data remains safe. There is no central database to dump; any partial data from servers is still encrypted and incomplete. According to Zyphe’s metrics, decentralized storage **reduces the overall breach risk by orders of magnitude**, roughly a 96% improvement in breach resistance over centralized storage methods.
- **Individual User Account Breach:** In the unfortunate case that a single user’s credentials or device are compromised, **only that user’s encrypted data** could potentially be accessed. No attacker can use one user’s credentials to leapfrog into a vast database of others’ information, because such a central database doesn’t exist. This containment contrasts with centralized systems where a single leaked administrator account or vulnerability could expose _millions_ of user records at once. Zyphe’s design inherently limits the blast radius of any single-user breach to that one user alone.
Overall, by geographically distributing encrypted data shards and requiring a distributed consensus to reassemble them, Zyphe creates a **near-impenetrable security mesh** around PII. Even a breach at one point yields nothing of value, and **systemic breaches become exceedingly unlikely**. This approach **virtually nullifies the mass data-theft scenarios** that plague centralized databases. It’s a level of security and risk reduction that centralized storage simply cannot match.
_(Notably, this decentralized model also simplifies compliance with data protection laws: user data shards stay within the user’s region or jurisdiction, satisfying stringent data residency requirements like GDPR by design. In other words, your European customers’ data stays in Europe, your Canadian data stays in Canada, etc., adding another layer of security and regulatory peace of mind.)_
### User Ownership and Enhanced Privacy Control
A decentralized data ecosystem also transforms the concept of data **ownership**, **putting users firmly in control of their own PII**. With Zyphe, once your data is verified and fragmented into the network, **you as the user retain the keys and rights to that information**, rather than a company holding it in a silo. Zyphe acts as a secure facilitator, but **does not have unilateral access to your personal data** after onboarding. This user-centric ownership model has several powerful implications:
- **Personal Data Autonomy:** You decide **who** can access your identity information and **when**. Sharing your data becomes a conscious, user-driven action (for example, consenting to share your verified credentials with a new service), rather than something that happens behind the scenes in a provider’s database. This **enhanced privacy control** means your sensitive documents aren’t floating around on various third-party servers without necessity. Zyphe’s system is built on privacy-by-design principles to ensure users have direct control over their data at all times.
- **No Unwanted Access or Backdoors:** Because of the encryption and distributed keys, **even Zyphe cannot access or read your PII** once it’s decentralized. There is no “backdoor” where a staff member or an internal breach could pull up your raw documents. This provides peace of mind that _only_ you (and those you authorize) ever see your personal data in readable form. It also prevents any single entity from monetizing or misusing your data, you truly **own your identity data** in the system.
- **Privacy and Compliance by Design:** The decentralization inherently enforces data minimization, only the necessary information is stored, and it’s stored in a dispersed way. This minimizes the privacy risks and lowers compliance burdens on businesses using Zyphe. Companies don’t have to hold caches of PII they might otherwise be liable for securing; instead, users hold their data and just grant permission as needed. This **privacy-first architecture** builds trust: end-users know their information isn’t being stockpiled and exploited, and businesses can demonstrate respect for user privacy from the ground up.
In summary, decentralization shifts the power over personal data back to its rightful owner, the individual. When clients use Zyphe’s decentralized KYC platform, they are empowering their users with transparency and control, which strengthens the business-user relationship. Users gain confidence knowing _their_ data is safe, not only from hackers but also from unnecessary access or misuse.
### One-Click KYC with Reusable Credentials: Security Meets Convenience
A major advantage of Zyphe’s decentralized approach is the introduction of **One-Click KYC** via **reusable credentials**. This feature is not just a usability improvement, it also enhances security by reducing how often sensitive data needs to be shared. Here’s how it works and why it’s better than traditional repeated KYC checks:
- **Verify Once, Reuse Securely:** With Zyphe, a user goes through the identity verification process **only once** to create a trusted digital credential. This might involve verifying a passport, driver’s license, selfie liveness check, etc., just like a normal KYC, but after this is completed, the verified identity data is encrypted and stored _decentrally_ as described above. The user is then issued a reusable KYC credential (essentially a cryptographic proof of their verified identity). When the user needs to verify their identity with another service or platform, they can simply **share this credential with one click**, no need to re-upload documents or re-enter personal details for each new account. The receiving service gets confirmation that the user’s identity is verified (via Zyphe’s attestation), **without the user’s raw PII ever changing hands again**. This dramatically cuts down on unnecessary data exposure.
- **Minimized Data Exposure:** In traditional KYC processes, a user might send copies of their passport, ID, proof of address, etc., to every company they do business with, leading to _multiple copies_ of their PII stored across the internet. Each additional copy and storage location is another potential breach point. One-Click KYC **solves this by minimizing data duplication**: the user’s sensitive documents are not repeatedly transmitted and stored in new databases. Instead, a secure reference or token is shared. This **reduces the amount of personal data shared by orders of magnitude**, thereby _greatly lowering the risk of data being exposed or stolen during KYC processes_. Essentially, your passport scan isn’t sitting in five different company databases, it stays encrypted in your control, and only a verifiable proof is shared.
- **Faster, Frictionless User Experience:** From a business perspective, one-click reusable KYC significantly **streamlines onboarding**. Users don’t drop out of signup flows due to KYC fatigue, since they can instantly satisfy KYC requirements with a click. This leads to higher completion rates, Zyphe reports up to **70% more users complete onboarding** when using reusable identity credentials versus traditional KYC methods. More completed onboardings mean more customers and revenue for businesses, achieved **without compromising security**. In fact, security is improved, as noted above, because less raw data is in transit or being repeatedly handled.
- **Cost and Efficiency Gains:** For businesses, the reusable KYC model cuts down on redundant verification checks and storage. They don’t need to store as much PII (which reduces compliance scope and costs), and they avoid the expense of performing KYC from scratch for returning users or across multiple services. This not only saves money but also means fewer opportunities for a mistake or leak. Overall, it’s a win-win: **better security and privacy** for users, along with **faster onboarding and lower overhead** for companies.
In summary, One-Click KYC powered by decentralized credentials combines **security with convenience**. It leverages the strength of Zyphe’s distributed network to **protect user data**, while simultaneously removing friction from the user journey. The result is a seamless yet secure verification process that stands in stark contrast to the cumbersome and risk-prone centralized KYC procedures of the past.
### Transparency, Trust, and Compliance in a Decentralized Ecosystem
Decentralization doesn’t just improve security and convenience, it also fosters **greater transparency and trust** between all parties involved. Because of its inherent design, Zyphe’s decentralized storage and verification system offers clarity into how data is handled, and robust safeguards that inspire confidence:
- **Auditability and Tamper-Proof Records:** Every action in a decentralized network can be logged in an immutable manner (often using blockchain-like ledgers or cryptographic audit trails). Zyphe employs **immutable logs and threshold encryption** so that any access to data requires consensus from multiple authorized parties. This creates a **tamper-proof record** of who accessed what and when, making the system highly transparent and audit-friendly. Neither data nor logs can be altered or deleted without detection. For regulated businesses, this means **simpler compliance audits**, you have a built-in, verifiable trail proving that sensitive data was handled properly and only accessed with proper authorization.
- **User Visibility and Consent:** Zyphe’s approach is _privacy-first and transparent by design_. Users can **see how their personal data is being used** and are given meaningful control over that usage. For instance, when a user shares their KYC credential with a new service, that consent and transaction can be made transparent to the user. There are no dark corners or hidden data flows as often seen in big centralized systems. This openness reassures users that their information isn’t being misused or accessed without their knowledge, further building trust in the platform.
- **No Central Honeypot for Misuse:** With no central database, there is no opportunity for internal bad actors or external spies to quietly collect large datasets of personal information. In a centralized model, whoever runs the database has enormous power (and could potentially be compelled or tempted to misuse data). In Zyphe’s decentralized model, **such risks are mitigated**, even the service provider _cannot_ unilaterally exploit user data. This strong stance on privacy and security **demonstrates to clients and end-users that the company values their data protection**, not just in words but through concrete architecture choices.
- **Improved Customer Trust and Brand Reputation:** Ultimately, using a decentralized, secure solution helps a business **build trust with its customers**. Users are increasingly concerned about how companies handle their PII, and rightfully so, data breaches and privacy scandals have eroded public confidence in many industries. Adopting Zyphe’s decentralized storage for PII sends a message that your organization is _proactively safeguarding user privacy_. Knowing that a service “doesn’t even have access to my data unless I allow it” is a powerful trust signal to customers. Moreover, by **eliminating single points of failure**, Zyphe’s system greatly reduces the chance of a devastating breach that could destroy customer trust overnight. In fact, Zyphe’s decentralized identity management is explicitly designed to **improve user trust by removing those single points of vulnerability**. Businesses that prioritize such privacy and security not only avoid costly breaches and compliance fines, but also **differentiate themselves in the market as trustworthy custodians of user data**.
In a decentralized ecosystem, **transparency is not an afterthought, it’s baked into the way data is stored and accessed.** This assures all stakeholders (users, businesses, regulators) that PII is handled with the utmost integrity. Companies leveraging Zyphe can confidently show that they _never_ put customer data at unnecessary risk, thereby meeting regulatory requirements and exceeding user expectations for privacy. The result is a stronger foundation of trust, which is invaluable in any client relationship.
### **Conclusion:** Decentralization = Better Security, Privacy, and Peace of Mind
Shifting from a centralized to a decentralized model for storing PII is a **paradigm shift in security and data ownership**. By decentralizing data storage and control, Zyphe achieves a level of security and privacy that traditional systems cannot match. Data breaches are no longer existential threats but contained incidents; users are no longer passive data subjects but active data owners. The **technical benefits** (encryption, sharding, redundancy, no single point of failure) translate directly into **business benefits**, drastically lower breach risk, easier compliance, reduced liability, and higher user trust and satisfaction.
In practical terms, **decentralized storage is simply safer for PII**. It means that even if one door is pried open, the vault as a whole remains secure. It means only those with permission can ever assemble the pieces of personal data, and even then under strict controls. It means an organization doesn’t have to wake up to a nightmare headline about millions of identities leaked, because such a leak is nearly impossible by design. And it means users can confidently share their information, knowing it isn’t being hoarded in yet another vulnerable silo.
By embracing this decentralized approach, **Zyphe and its clients stand at the forefront of data security and privacy**. They offer customers a solution that is _not only more secure_, but also _more respectful_ of individual privacy. In an era of daily data breaches and increasing privacy regulations, this approach isn’t just better, it’s the future of how sensitive information should be handled. **Decentralization makes PII storage better, safer, and smarter**, creating a win-win for businesses and the people they serve.
---
## Frequently Asked Questions(Frequently-asked-questions)
## What is a Decentralized Storage Vault?
A **User Vault** is a secure, personal space for verified and encrypted information, such as identity documents and biometrics collected during verification. It allows users to safely store and manage their data while giving companies a compliant way to access it when authorized.
Access is always controlled by the user. Data can only be shared with parties explicitly granted permission or those who hold the unique Vault Access Key.
Unlike centralized storage, a User Vault is **distributed and decentralized**: information is broken into fragments (shards) and stored across multiple nodes in Zyphe’s network. Each shard is encrypted using an **n-out-of-m threshold scheme**, so no single node can ever view or reconstruct the underlying data. Only authorized users can reassemble and decrypt the information, ensuring there is no single point of failure and dramatically reducing the risk of breaches or unauthorized access.
For onboarding, users benefit from faster verification: if an identity document in the vault is still valid and accepted by a platform, they can reuse it without repeating KYC.
All data is geo-located to meet residency requirements and protected with industry-grade security, including **AES‑256** encryption. By design, Zyphe’s decentralized infrastructure removes central points of vulnerability, lowering both compliance overhead and data breach risks while keeping privacy fully in the user’s control.
## How should I store my Vault Access Key?
You are welcome to store the Vault Access Key to your own decentralized storage vault in whichever format you prefer, though we recommend using a secure password manager that also includes biometric validation or a passkey.
In the event of a future onboarding using Zyphe, using this passcode, you will be able to save time and avoid redoing KYC if the IDs in your vault are still valid and include an acceptable form of ID for the platform that you’re onboarding with. If you lose your Vault Access Key, then you may be required to redo your KYC the next time you onboard with a platform that uses Zyphe.
## Is it safe to send users a Vault Access Key via email?
Yes, sending your Vault Access Key by email is considered safe enough to balance ease of use with strong security. We chose this method so that you can receive the key quickly and conveniently, without complicated steps, while still ensuring your information remains protected.
The Vault Access Key on its own does not provide access to your data or account. It can only be used in combination with your existing platform credentials, which stay private and secure. This means that even in the unlikely event the email were intercepted or the key leaked, it cannot be used directly to access your storage or personal information.
In addition, our emails are transmitted over encrypted connections (TLS) whenever supported by mail providers, helping to protect them during delivery.
To keep your account as safe as possible, we recommend that you:
Do not forward or share your Vault Access Key
Store it securely (for example, in a password manager)
Delete the email once you’ve saved the key safely
This approach ensures that the Vault Access Key is both **convenient to use** and **secure against misuse**.
## Does my Zyphe Vault Access Key grant access to the platform that I'm onboarding with?
The Vault Access Key you received is only for your identity information stored in Zyphe, which can be conveniently reused for future onboarding onto other platforms that also use Zyphe for identity verification. It does not grant access to the platform that you’re onboarding with. You will separately need the login information for that specific platform.
---
## Introduction
Zyphe is identity infrastructure built so organizations can **verify, reuse, and monitor** trust, without treating personal data as a free-for-all.
One platform covers:
- **Identity verification (KYC)**: document, liveness, address, phone, forms, and more, composed into flows
- **Business verification (KYB)**: companies, directors, and beneficial owners
- **Reusable credentials**: one-click KYC and KYC Passport so partners reuse an existing verification with user consent
- **AML screening & monitoring**: sanctions, PEP, adverse media, and ongoing risk
- **Transaction monitoring**: rules, lists, and Allow / Review / Block decisions
Privacy is not a slogan here: PII is stored with user-centric controls, access is role-based, and sharing is grant-driven. For the architecture behind that, see [Decentralized Identity](/docs/decentralized-identity).
---
## Start here
| Path | Who it's for | Time |
| -------------------------------------------------------------------------- | ---------------------------------------------- | ---------------------- |
| **[Quickstart](/docs/quickstart)** | Anyone shipping a first verification | Shortest linear path |
| **[Choosing an integration method](/docs/choosing-an-integration-method)** | Engineers deciding hosted link vs SDK vs agent | Decision page |
| **[Sandbox and go-live](/docs/guides/sandbox-and-go-live)** | Teams ready for production | QA + cutover checklist |
| **[Guides](/docs/guides/dashboard)** | Configuring and operating each capability | Task-oriented how-tos |
| **[Statuses and codes](/docs/reference/statuses-and-codes)** | Interpreting webhooks and API outcomes | Reference |
| **[API reference](/docs/openapi/zyphe-sdk)** | Coding against the exact contract | OpenAPI + schemas |
---
## Core objects
These are the concepts you will see across the product and API:
| Object | What it is |
| ------------------------ | --------------------------------------------------------------------------------- |
| **Flow** | The sequence of verification steps a user completes (document, liveness, PoA, …) |
| **Verification request** | One run of a flow for a specific user |
| **Flow result** | The reviewed outcome, scores, and collected data (subject to role and PII access) |
| **Webhook** | Signed real-time notification when a result or status changes |
| **Score** | Explainable risk or quality signal derived from tags, factors, and checks |
| **Transaction** | An activity event evaluated by transaction-monitoring rules |
Deeper model: [Verification process](/docs/verification-process) · [Data flows](/docs/data-flows)
---
## Products
| Product | Outcome | How it works | Guide |
| -------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Flows / KYC** | Onboard users with composable checks | [Verification process](/docs/verification-process) · [Document](/docs/how-it-works/document-verification) · [Liveness](/docs/how-it-works/liveness) | [Create a flow](/docs/guides/create-a-flow) |
| **Proof of address** | Verify residency evidence | [PoA](/docs/how-it-works/proof-of-address) | [Configure PoA](/docs/guides/create-a-flow/proof-of-address) |
| **KYB** | Verify businesses and ownership | [KYB](/docs/how-it-works/kyb) | [Business verification](/docs/guides/kyb) |
| **Reusable credentials** | Reuse trust across partners | [One-click KYC](/docs/reusable-credentials-one-click-kyc) | [KYC Passport](/docs/guides/kyc-passport) |
| **AML** | Screen and monitor financial crime risk | [AML](/docs/aml) | [Risk scoring](/docs/aml/risk-scoring) · [Monitoring](/docs/aml/monitoring) |
| **Transaction monitoring** | Continuous Allow / Review / Block | [TM concepts](/docs/transaction-monitoring) | [TM operation](/docs/guides/transaction-monitoring) |
| **Scoring & review** | Turn signals into decisions | - | [Scoring](/docs/guides/scoring) · [Manual review](/docs/guides/manual-review) |
---
## Who this documentation is for
**Engineers**: integrate with a [public link](/docs/choosing-an-integration-method#public-link), [SDK](/docs/guides/sdk), [webhooks](/docs/guides/webhook), or an [MCP-compatible AI agent](/docs/guides/model-context-protocol-mcp). Start with the [Quickstart](/docs/quickstart).
**Compliance & operations**: review results, configure scores, manage AML and transaction-monitoring cases, and enforce [role-based PII access](/docs/pii-access-control). See [Manage users](/docs/guides/manage-users) and [Scoring](/docs/guides/scoring).
**Partners & networks**: invite organizations and reuse verifications with user consent via [KYC Passport](/docs/guides/kyc-passport) and [reusable credentials](/docs/reusable-credentials-one-click-kyc).
---
## How the docs are organized
| Section | Intent |
| -------------- | ---------------------------------------------------------------------- |
| **Quickstart** | One opinionated path from zero to a reviewed result |
| **Overview** | What the product is, why it works this way, conceptual data flows |
| **Guides** | How to configure and operate each capability |
| **Reference** | Statuses and codes, OpenAPI, [LLM-friendly docs](pathname:///llms.txt) |
Rule of thumb: deciding _whether_ → Overview · fastest _try_ → Quickstart · _configure_ → Guides · _code the contract_ → Reference.
---
## Integrations
This section covers third-party systems and protocols that Zyphe interoperates with.
- **[Italian Digital Identity Ecosystem (SPID and CIE)](./italian-digital-identity-ecosystem-spid-and-cie.md)**: verify Italian users via the official SPID and CIE identity providers.
---
## Italian Digital Identity Ecosystem: SPID and CIE
SPID (Sistema Pubblico di Identità Digitale) and CIE (Carta d'Identità Elettronica) are two key components of the Italian digital identity ecosystem, designed to simplify access to online services for citizens and businesses.
## SPID (Sistema Pubblico di Identità Digitale - Public Digital Identity System)
- **What it is:** SPID is a set of credentials (username, password, and often a second factor like an OTP) that allows citizens and businesses to access online services of the Italian public administration and private entities that have joined the system. It's not a physical card but a digital identity.
- **How it works:** Users obtain their SPID identity from accredited "Identity Providers" (e.g., Poste Italiane, Aruba, Infocert). Once registered, they use these credentials to log in to various online services without needing separate accounts for each.
- **Purpose:** To provide a single, secure, and unified digital identity for accessing a wide range of public and private online services, streamlining bureaucracy and enhancing digital interaction.
## CIE (Carta d'Identità Elettronica - Electronic Identity Card)
- **What it is:** The CIE is the Italian electronic identity card, a physical smart card that serves as a personal identification document. Beyond its physical use for identification, it incorporates a microchip that can be used for digital authentication.
- **How it works:** The CIE can be used in two main ways for digital services:
- **Level 1 (Desktop):** By physically inserting the card into an NFC-enabled card reader connected to a computer and entering a PIN.
- **Level 2 (Mobile):** Using an NFC-enabled smartphone with the "CIE ID" app to read the card and authenticate, often with fingerprint or facial recognition as a second factor.
- **Purpose:** To serve as a secure physical and digital identification document. It provides a robust authentication method for online services, similar to SPID but based on a physical token (the card).
## Key Differences and Relationship:
- **Nature:** SPID is purely a digital identity (credentials), while CIE is a physical document with digital capabilities.
- **Issuance:** SPID is issued by private Identity Providers; CIE is issued by the Ministry of Interior through municipalities.
- **Authentication:** Both offer secure authentication to online services. SPID is generally more widespread for quick access, while CIE offers a higher level of assurance due to its physical token component.
- **Interoperability:** Both are part of the broader Italian digital identity strategy and can often be used interchangeably to access the same online services. The trend is towards deeper integration, with the CIE becoming a potential "SPID provider" in itself.
In essence, SPID provides a flexible and widely adopted digital login, while the CIE offers a highly secure, hardware-based authentication method, both working together to underpin Italy's digital public services.
## Verification process
When a user starts a SPID/CIE verification step on the Zyphe platform is prompted to log in with their SPID or CIE accounts. Once the authentication happens, the user is asked to confirm that he is going to share data with the Zyphe platform. After the confirmation, the platform collect the information from the identity provider and store the underlying information in the User Vault the same way we do for Document Verification.
---
## PII Access Control
## Overview
Zyphe applies role-based access control (RBAC) to Personally Identifiable Information (PII) so that
sensitive customer data is only visible to the people who need it. This page explains the
**principles** behind that model. For the concrete roles and the exact permission matrix (who can
do what in the dashboard and API), see [Manage Users](/docs/guides/manage-users).
## Problem statement
In many KYC/KYB systems, everyone with dashboard access can see every customer's PII. That creates
avoidable risk:
- **Insider threat**: internal actors with legitimate access can exfiltrate large volumes of
sensitive data.
- **Over-privileged access**: people with no need to view PII can see it anyway.
- **Compliance risk**: broad access makes data-minimization obligations under GDPR, CCPA, and
similar regimes hard to meet.
- **Weak audit trail**: without granular controls it is difficult to reconstruct who accessed what,
and when.
## How Zyphe reduces PII exposure
Zyphe addresses this on two fronts:
1. **Decentralized storage.** PII lives in the user's own vault, not in a central Zyphe database.
Only the minimum attributes required for a given operation are read, and every access is
recorded in a hash-chained audit trail. See
[Decentralized Identity](/docs/decentralized-identity) and
[Why decentralized PII storage](/docs/decentralized-identity/why-decentralized-pii-storage).
2. **Role-based access.** Within your organization, what each member can see and do is governed by
their **role**. Roles are least-privilege by default: a member is granted the narrowest set of
permissions that lets them do their job, and elevated access is the exception rather than the
rule.
## Roles
Access is governed by the organization roles Zyphe enforces today:
| Role | Typical use |
| --------------- | --------------------------------------------------------------------- |
| **Admin** | Organizational management and oversight; full access. |
| **Operator** | Reviewing and operating on verification results and AML cases. |
| **AML Officer** | AML monitoring and case management. |
| **Developer** | Integration work, flows, API keys, webhooks. |
| **Partner** | External collaborators, limited to flows explicitly shared with them. |
The authoritative, per-permission breakdown of these roles, including what each can access and how
partner flow access is granted, is maintained in **[Manage Users](/docs/guides/manage-users)**.
Treat that page as the single source of truth; this page covers only the principles.
:::info Least privilege in practice
Default new members to the lowest role that lets them work, review elevated roles periodically, and
keep the number of full-access accounts small. Grant broader access deliberately, not by default.
:::
## Webhooks and PII
Webhook payloads respect your organization's PII configuration. You can choose how much a webhook
carries:
- **No PII (recommended)**: the webhook signals that a result is ready; PII is fetched separately,
subject to role permissions.
- **Identifier only**: the webhook includes a session/result identifier and nothing sensitive.
- **PII included**: only for organizations that explicitly opt in and accept the added handling
responsibility.
See [Webhooks](/docs/guides/webhook) for delivery and payload details.
## Best practices
**For organizations**
1. Assign full-access (Admin) roles sparingly and secure those credentials with strong
authentication.
2. Review who holds elevated roles on a regular cadence.
3. Monitor for unusual access patterns.
4. Default to the least-privileged role and grant more only when there is a demonstrated need.
**For developers**
1. Never cache PII in client-side storage or logs.
2. Check access on every request rather than assuming it from a previous one.
3. Handle missing PII gracefully, your UI should work when PII fields are absent for the current
role.
4. Test your integration against every role your users may have.
## Support
For questions about PII access control, contact **support@zyphe.com**.
---
## Reusable Credentials
## One click KYC
Zyphe's decentralized KYC (Know Your Customer) is an innovative approach to the traditional KYC process, which involves collecting and verifying customer information to comply with legal and regulatory requirements. Here's a simplified explanation of how reusable credentials in a decentralized KYC system works:
1. **Initial Verification**:
- When a user first interacts with a company that requires KYC verification, they undergo a standard verification process. This could involve submitting identification documents, proof of address, and other required information.
2. **Credential Issuance**:
Once the user's information is verified, is encrypted, sharded and stored in a secure, decentralized manner, often on a blockchain or a distributed ledger, ensuring they are tamper-proof and owned by the user.
3. **Data Ownership and Control**:
The user has control over their digital credentials. They can choose who to share their credentials with and can manage permissions through a simple interface.
4. **Reuse of Credentials**:
- When the user interacts with a new company requiring KYC verification, they can share their previously verified credentials instead of going through the verification process all over again.
5. **Benefits**:
- **Time-Saving**: Reusing credentials can significantly speed up the KYC process for users and companies.
- **Cost Efficiency**: Companies save resources as they don't have to repeatedly conduct the same verification processes.
- **Privacy Enhancement**: Users share only the necessary information, and the decentralized nature of the system can provide better data privacy and security.
6. **Interoperability**:
- The decentralized KYC systems are often designed to be interoperable, allowing various companies and platforms to utilize the same set of reusable credentials, fostering a seamless user experience across different services.
This setup creates an ecosystem where KYC processes become more user-centric, efficient, and privacy-preserving, with the ability to reuse verified credentials across multiple platforms and services.
## How it works
When a user arrives at a company that requires KYC, Zyphe checks whether they already hold an
eligible credential and, with the user's consent, shares it through an **access grant** instead of
re-running verification.
```mermaid
sequenceDiagram
autonumber
actor User
participant CW as Company Website
participant C as Company
participant ZF as Zyphe frontend
participant ZB as Zyphe backend
participant DCS as Decentralized Storage
CW-->>+User: share a link
User->>+ZF: starts a Flow
ZF->>+ZB: check for eligible documents
ZB-->>+ZF: send list of eligible documents
ZF-->>+User: ask the user
User->>+ZF: unlock vault
ZF->>+ZB: grant access to the company
ZB-->>+C: send access grant
C->>+ZB: request data using access grant
ZB->>+DCS: request data
DCS-->>+ZB: return data
ZB-->>+C: return data
```
1. **Redirect through company website**: The user starts on the dashboard of a company that needs to perform KYC, and is redirected to Zyphe to begin.
2. **Starts the onboarding flow**: The user lands on the platform and starts the onboarding flow.
3. **Check eligible documents**: The frontend asks the backend whether there are eligible documents for that flow.
4. **Return list of eligible documents**: The backend checks the user vault for eligible documents.
:::info
The check runs against a metadata table that holds only references and the minimum information needed for this check. The backend does not have access to any personal information.
:::
5. **Ask the user**: If eligible documents exist, the platform asks whether the user wants to reuse a previously generated credential or verify again.
6. **Unlock vault**: To reuse a credential, the user unlocks the vault. This can use different authentication mechanisms, a passphrase, a Vault Access Key, or a passkey. Advanced users can run in BYOK mode where the private key is bound to a passkey on their device; traditional users rely on a managed key handled under a strict no-log paradigm. See [Data Sharing and Encryption](../decentralized-identity/data-sharing-and-encryption.md).
7. **Grant access**: An **access grant** is created. Under the hood it is a **proxy re-encryption key** that lets Zyphe (the proxy) transform ciphertext encrypted under the user's key into ciphertext that only the requesting company can decrypt. Zyphe never sees the plaintext.
8. **Send grant to the company**: The access grant is connected to the company dashboard.
9. **Request user data**: The company requests the user data through its dashboard on the Zyphe platform.
10. **Request data to DCS**: The system pulls the relevant data from decentralized storage, where it is securely stored.
11. **Return data from DCS**: The requested data is retrieved from the decentralized storage network.
12. **Display data in the company dashboard**: The retrieved data is returned to the company dashboard.
---
## Transaction Monitoring(Transaction-monitoring)
Transaction Monitoring is Zyphe's real-time payment-screening capability. Where KYC and KYB verify
**who** a user is at onboarding and AML screens them against sanctions, PEP, and adverse-media
lists, Transaction Monitoring watches **what they do afterwards**: evaluating each payment event
as it happens and returning an **Allow**, **Review**, or **Block** decision before your platform
moves any funds.
Because Zyphe already verifies your users, every monitored transaction is tied to a **verified
identity**. A single rule can combine transactional signals (amount, counterparty, country,
timing) with identity signals (KYC risk score, account age). This is what closes the loop between
onboarding, screening, and ongoing activity: **full-cycle compliance** from the first
verification to every subsequent payment.
## The decision model
Your integration reports a transaction event; Zyphe evaluates it synchronously against your
organization's enabled **rules**. Each rule that fires contributes a probability, and the
contributions are combined into a single risk probability with a probabilistic union (noisy-OR):
```text
P(risk) = 1 − ∏ (1 − pᵢ)
```
`P(risk)` is compared against your organization's thresholds to produce the verdict:
| Condition | Decision |
| ------------------------------------ | -------- |
| `P(risk)` below the review threshold | Allow |
| `P(risk)` ≥ review threshold | Review |
| `P(risk)` ≥ block threshold | Block |
This is the **same combinator** used by Zyphe's generalized scoring engine. See
[Scoring](/docs/guides/scoring) for the underlying mathematics of tags, banding, and
thresholds. Transaction Monitoring applies it to per-transaction rules rather than to
verification-flow signals.
## Privacy-preserving by design
Like the rest of Zyphe, Transaction Monitoring works over decentralized identity: only the minimal
attributes needed to evaluate a rule are read from the user's vault, and every evaluation is
written to a hash-chained audit trail. Personal data is not centralized to enable monitoring.
## Sandbox-first
Transaction Monitoring can be enabled in **sandbox only**: a prospect gets the full rule builder,
templates, lists, and evaluation loop against synthetic data, with no production entitlement and
no data-processing agreement required. This makes it possible to design and backtest a rule set
before any real payment traffic or contract is in place.
## Where to go next
This page is the conceptual overview. To configure and operate Transaction Monitoring, reporting
events, authoring rules, instantiating regulator-informed templates, curating lists, tuning
thresholds, and handling alerts. See the **[Transaction Monitoring
guides](/docs/guides/transaction-monitoring)**.
---
## Verification Process
## Overview
The onboarding flow can be composed by a single step or multiple steps. Any onboarding flow contains at least one **Identity Verification** step (**IDV**) such as a Document Verification or a login with eID. Typically, a complete identity verification flow includes both a Document Verification step (to scan and validate the identity document) and a Liveness Verification step (to ensure the person is real and matches the document). All critical operations such as document scanning and liveness checks are always performed on our platform through a secure HTTPS connection. That way we can add a first layer of security ensuring that the acquisition is performed properly, reducing the risk of deep fake.
The platform provides two distinct methods to integrate with your app:
### Public Web Link
Each onboarding flow is associated with a unique public web link that can be shared with the users.
Once they click on it, they are redirected to a designated flow on our platform. In this setup, users must authenticate via email by manually entering their email address, clicking the link sent to their inbox, and completing the flow.
It involves the following steps:
- The user clicks a button on your website containing the link to the proper verification flow on the Zyphe Platform
- The user is redirected to the Zyphe platform
- The user authenticate on the Zyphe platform with his email
- The user performs any verification step directly on the Zyphe platform
- Zyphe notify your backend about the verification result via the Webhook and make the result accessible within the dashboard
Here, you can find a video that demonstrates integration and identity verification using the public web link:
### SDK
The SDK integration supports 2 different approaches:
#### Dynamic link generation
Using the nodejs SDK it's possible to dynamically generate the verification session together with a link that redirects to our platform to complete the verification.
```mermaid
sequenceDiagram
actor User
participant AF as App frontend
participant AB as App backend (nodejs sdk)
participant ZB as Zyphe backend
participant ZF as Zyphe frontend
User->>+AF: start verification
AF->>+AB: request
AB->>+ZB: create verification session
ZB-->>+AB: verification session token
AB-->>+AB: generate verification session link
AB-->>+AF: verification session link
AF-->>+User: redirect
User->>+ZF: perform verification
```
#### Embed into your web app
Using the browser SDK it's possible to embed the verification flow directly into your application through an iframe.
```mermaid
sequenceDiagram
actor User
participant AF as App frontend (browser sdk)
participant ZB as Zyphe backend
User->>+AF: start verification
AF->>+ZB: create verification session
ZB-->>+AF: verification session token
AF-->>+AF: render iframe
AF-->>+User: ready
User->>+AF: perform verification
```
:::info
The browser integration is done through an iframe to ensure that the javascript that is executed is not manipulated and the communication between the SDK and the backend is properly authenticated.
:::