> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.withpersona.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withpersona.com/_mcp/server.

# Embedded Flow Overview

> Embed Persona's identity verification flow directly in a web page.

#### What's Embedded Flow?

The embedded flow is a drop-in module that enables you to seamlessly verify individuals within your web page. It allows individuals to easily verify themselves without leaving their current experience. The flow securely collects and verifies the individual without redirecting away from your website.

![embedded-flow](https://assets.withpersona.com/f_auto,q_auto/developer-docs/images/embedded-flow.png)

The latest version of the SDK is:
[![Persona SDK latest](https://img.shields.io/npm/v/persona?label=persona\&color=4700EB)](https://www.npmjs.com/package/persona)

There are two ways to use Embedded Flow. Both require some code:

1. **Generate inquiries from an inquiry template** (Minimal code required)
   * Embed the web SDK and configure it with your inquiry template ID.
   * A new inquiry is created each time a user starts the flow.
   * Best for: small numbers of users, simple use cases
   * Warning: Users who load your page multiple times will create duplicate inquiries.
2. **Pre-create inquiries via API** (More code required)
   * Embed the web SDK.
   * For each new user, create a new inquiry ID via API, then pass the inquiry ID to the SDK.
   * Best for: High volume and/or personalized experiences
   * Recommended for production use

Note that you can get started with the simpler implementation, and build up to the more scalable approach later.

## Tutorials

* [Tutorial: Embedded Flow with Inquiry Template](/tutorial-embedded-flow-inquiry-template)
* [Tutorial: Pre-create inquiries for Embedded Flow](/tutorial-embedded-flow-precreate)

## Quick reference

### Embed Code Snippet

Creating inquiries through the [Embedded](/embedded-flow) integration can be easily achieved with a short code snippet. You'll only need your inquiry template ID which can be found in the [Documentation](https://app.withpersona.com/dashboard/getting-started/embedded-flow) section of your Dashboard.

**`JavaScript (NPM)`**

```javascript JavaScript (NPM)
import Persona from 'persona';

const client = new Persona.Client({
  templateId: "<your template ID starting with itmpl_>",
  referenceId: "<your reference ID for this user>",
  environmentId: "<your environment ID starting with env_>",
  onReady: () => client.open(),
  onComplete: ({ inquiryId, status, fields }) => {
    // Inquiry completed. Optionally tell your server about it.
    console.log('Sending finished inquiry ' + inquiryId + ' to backend');
  },
  onCancel: ({ inquiryId, sessionToken }) => console.log('onCancel'),
  onError: (error) => console.log(error),
});
```

**`HTML (CDN)`**

```html HTML (CDN)
<!DOCTYPE html>
<html>
  <head>
    <!-- Replace "X.Y.Z" with the Inquiry SDK version you want to use. -->
    <!-- 
        It's best practice to provide an integrity attribute. 
        Learn more here: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
        Or copy the code snippet from the Persona dashboard, which provides the hash for you.
    -->
    <script src="https://cdn.withpersona.com/dist/persona-vX.Y.Z.js" integrity="your-integrity-hash" crossorigin="anonymous"></script>

    <!-- charset and viewport meta tags are required! -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>

  <body>
    <!-- Initialize the Persona client in whichever way is appropriate for your application. -->
    <script>
      const client = new Persona.Client({
        templateId: "<your template ID starting with itmpl_>",
        referenceId: "<your reference ID for this user>",
        environmentId: "<your environment ID starting with env_>",
        onReady: () => client.open(),
        onCancel: ({ inquiryId, sessionToken }) => console.log('onCancel'),
        onError: (error) => console.log(error),
        onEvent: (name, metadata) => {
          if (name === 'start') {
            // Collect and save the inquiry ID for future use
            inquiryId = metadata["inquiryId"]
          }
        },
        onComplete: ({ inquiryId, status, fields }) => {
          // Inquiry completed. Optionally tell your server about it.
          console.log('Sending finished inquiry ' + inquiryId + ' to backend');
          // Optionally, cleanup the client to avoid memory leaks.
          // client.destroy();
        },
      });
    </script>
  </body>
</html>
```

To permit the Persona iframe to render on your domain, see [Security > Embedding the Persona iframe](/embedded-flow-security#embedding-the-persona-iframe).

### Callbacks

You can also use optional callbacks for advanced [Event Handling](/embedded-flow-client-callbacks).

**`javascript`**

```javascript javascript
const client = new Persona.Client({
  templateId: "<your template ID starting with itmpl_>",
  environmentId: "<your environment ID starting with env_>",
  onReady: () => client.open(),
  onEvent: (name, meta) => {
    switch (name) {
      case 'start':
        console.log('Received event: start with inquiry ID ' + meta.inquiryId);
        break;
      default:
        console.log('Received event: ' + name + ' with meta: ' + JSON.stringify(meta));
    }
  }
});
```

### Methods

Use the client's [Methods](/embedded-flow-client-methods) to show, hide, or cleanup the embedded flow widget.

**`javascript`**

```javascript javascript
const client = new Persona.Client({
	templateId: "<your template ID starting with itmpl_>",
  environmentId: "<your environment ID starting with env_>",
  onComplete: ({ inquiryId, status, fields }) => {
	  // Inquiry completed. Optionally tell your server about it.
	  console.log('Sending finished inquiry ' + inquiryId + ' to backend');
	  fetch('/server-handler?inquiry-id=' + inquiryId);
  }
});

function openClient() { client.open(); }
function cancelClient() { client.cancel(true); }
```