> 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.

# Client Callbacks

> Handle Embedded Flow UI events without relying on callbacks for critical business logic.

> **Warning**
>
> #### Do not rely on callbacks for critical business logic
>
> SDK callbacks are intended for coordination between your app's UI and Persona's UI (e.g. opening and closing the flow UI). They do NOT guarantee that data are up-to-date, and cannot be reliably used to guarantee data integrity. Webhooks should be used for logic that depends on Inquiry state.
>
> For more information, see [Accessing Inquiry status and data](/accessing-inquiry-status#webhooks-vs-sdk-callbacks).

## onLoad & onReady

The `onLoad` callback is called when the `iframe` finishes loading and is ready to be displayed. It does not take any arguments.

The `onReady` callback is called when the inquiry flow is ready for user interaction. It does not take any arguments.

## onCancel

The `onCancel` callback is called when an individual cancels the inquiry flow before completion. It receives a single object argument with the following properties:

**`inquiryId`** `string`

The ID of the inquiry used in this instance of the flow. Will be `undefined` if the flow is canceled before an inquiry is created.

---

**`sessionToken`** `string`

A token that can be used to resume the inquiry. Will be `undefined` if the flow is canceled before an inquiry is created.

---

## onComplete

The `onComplete` callback is called when the inquiry has completed the inquiry flow and the individual clicks on the complete button to close the flow.

> **Warning**
>
> The purpose of this callback is to signal when the user has completed the Persona flow and should be sent back to your application. `onComplete` is **not guaranteed to be called**; it is possible that the user never presses the complete button.
>
> `onComplete` receives the current status of the inquiry as an argument. These values are passed for convenience, and are **not guaranteed to be up to date**. For instance, a [Workflow](/api-reference/workflows) may have been executed between when the inquiry was completed and when the user pressed the complete button, resulting in a status change. If you need the most up to date state of the inquiry, please use [Webhooks](/webhooks).

It receives a single object argument with the following properties:

**`inquiryId`** `string`

The ID of the inquiry used in this instance of the flow.

---

**`status`** `string`

The status of the completed inquiry (e.g. `'completed'`, `'failed'`).

---

**`fields`** `object`

A map of field values. See [Fields](/embedded-flow-fields) documentation.

---

## onError

The `onError` callback is called in response to errors in the inquiry flow that prevent the inquiry flow from being usable. These generally occur on initial load.

`onError` is not fired for network errors (e.g. dropped requests from bad connections, blocked requests due to application security settings, etc.)

It receives a single object argument with the following properties:

**`status`** `number`

The HTTP error status, if applicable. The value will be `0` if the error was an application error.

---

**`code`** `string`

A short string describing the error. See the error codes below.

---

### Error codes

| Error code            | Status   | Meaning                                                                                                                                                                                                                                                                                                                                                                  |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `'application_error'` | `0`      | An internal error occurred in the Persona web application. Please contact support.                                                                                                                                                                                                                                                                                       |
| `'camera_error'`      | `1`      | A black camera screen was detected during a capture step. **This error is noisy** — it can fire frequently due to transient rendering, permissions, hardware conditions, or dark lighting conditions, and does not always indicate a true failure. Treat it as a signal for monitoring rather than a hard error, and avoid using it to drive critical logic or alerting. |
| `'invalid_config'`    | `400`    | The `persona` client was initialized with invalid arguments.                                                                                                                                                                                                                                                                                                             |
| `'unauthenticated'`   | `409`    | An inquiry was resumed without a valid `sessionToken`. Retrieve one from the external API and pass it to the client.                                                                                                                                                                                                                                                     |
| `'inactive_template'` | `422`    | An attempt was made to create an inquiry from an inactive template. Activate the template before attempting to create inquiries.                                                                                                                                                                                                                                         |
| `'unknown'`           | `number` | Catch-all error.                                                                                                                                                                                                                                                                                                                                                         |

## onEvent

The `onEvent` callback is called at certain points in the Persona flow. It takes two arguments: an `eventName` string and a `metadata` object.

> **Warning**
>
> `onEvent` is sometimes passed the current state of the inquiry for convenience purposes. These values are not guaranteed to be the latest values on the inquiry, and thus should not be used for critical logic. If your business logic depends on granular verification and inquiry status changes, we recommend using [Webhooks](/webhooks).

By default, only legacy events are forwarded to `onEvent`. To receive newer events like `step-transitioning`, `click`, and `form-update`, you must opt in using the [`eventsAllowlist`](/embedded-flow-parameters#other-parameters) parameter (available in SDK `5.8.0`+):

* `eventsAllowlist: 'all'` — receive all events.
* `eventsAllowlist: [Event.Click, Event.StepTransitioning, ...]` — receive only the specified events.

### Event reference

#### `start`

Triggered when an Inquiry object has been created in the client. Dynamic Flow Templates will send this event at the same time as `'ready'`, and it will not indicate user interaction. Legacy 2.0 Templates will send this event only when the user clicks 'Continue' on the start screen.

**`metadata.inquiryId`** `string`

The ID of the inquiry.

---

#### `document-camera-select`

The individual is opening the camera for the government ID verification.

**`metadata.stepData`** `object`

Data about the current step.

---

**`metadata.stepData.idClass`** `string`

The class of the government ID being captured, if known.

---

#### `document-camera-capture`

The individual took a photo for the government ID verification.

**`metadata.stepData`** `object`

Data about the current step.

---

**`metadata.stepData.idClass`** `string`

The class of the government ID being captured.

---

**`metadata.stepData.files`** `array`

An array of objects describing the captured files.

---

**`metadata.stepData.files[].side`** `string`

Which side of the document was captured.

---

**`metadata.stepData.files[].captureMethod`** `string`

How the document was captured (e.g. `'manual'`, `'auto'`).

---

#### `document-upload`

The individual uploaded a government ID photo.

**`metadata.stepData`** `object`

Data about the current step.

---

**`metadata.stepData.idClass`** `string`

The class of the government ID being uploaded.

---

**`metadata.stepData.files`** `array`

An array of objects describing the uploaded files.

---

**`metadata.stepData.files[].side`** `string`

Which side of the document was uploaded.

---

**`metadata.stepData.files[].captureMethod`** `string`

How the document was captured.

---

#### `documents-save-successful`

The inquiry flow successfully saved captured documents.

**`metadata.stepData`** `object`

Data about the current step.

---

**`metadata.stepData.idClass`** `string`

The class of the government ID that was saved.

---

**`metadata.stepData.files`** `array`

An array of objects describing the saved files.

---

**`metadata.stepData.files[].side`** `string`

Which side of the document was saved.

---

**`metadata.stepData.files[].captureMethod`** `string`

How the document was captured.

---

#### `documents-save-fail`

The inquiry flow failed to save captured documents.

**`metadata.stepData`** `object`

Data about the current step.

---

**`metadata.stepData.idClass`** `string`

The class of the government ID that failed to save.

---

**`metadata.stepData.files`** `array`

An array of objects describing the files that failed to save.

---

**`metadata.stepData.files[].side`** `string`

Which side of the document.

---

**`metadata.stepData.files[].captureMethod`** `string`

How the document was captured.

---

#### `selfie-camera-select`

The individual is opening the camera for the selfie verification.

#### `selfie-camera-capture`

The individual took a photo for the selfie verification.

**`metadata.stepData`** `object`

Data about the current step.

---

**`metadata.stepData.files`** `array`

An array of objects describing the captured selfie files.

---

**`metadata.stepData.files[].pose`** `string`

The pose of the selfie.

---

**`metadata.stepData.files[].captureMethod`** `string`

How the selfie was captured.

---

#### `selfie-record-upload`

The individual uploaded a selfie photo. This is allowed when their browser does not support embedded video.

#### `load-camera-failed`

The inquiry flow failed to load the camera during a government ID capture, selfie, or document upload flow. This can happen due to missing permissions or a hardware error.

#### `camera-permissions-check-result`

The individual dismissed the camera permissions dialog on a capture step — by continuing, pressing Escape, or clicking outside the dialog — or clicked the check button on the camera support screen shown after a missing permission was detected. Permissions are re-read at that moment, so this reflects whether the individual actually granted access rather than what was requested earlier. It is sent at most once per dismissal. Legacy 2.0 Templates do not send this event from their camera support screen.

**`metadata.hasCameraPermission`** `boolean`

Whether camera permission has been granted.

---

**`metadata.hasMicrophonePermission`** `boolean`

Whether microphone permission has been granted. This is reported independently of `requestedMicrophone`, so it may be `false` on flows that never ask for the microphone.

---

**`metadata.requestedMicrophone`** `boolean`

Whether the flow asks for microphone access in addition to the camera. This is only `true` when the step both records video and has audio enabled; when `false`, `hasMicrophonePermission` can be ignored.

---

#### `one-time-link-sent`

The individual sent a one time link to their mobile device.

#### `one-time-link-start`

The individual resumed their flow on their mobile device using a one-time link.

#### `one-time-link-exit`

The individual returned to their initial device after sending a link to their mobile device.

#### `complete`

The individual is exiting the flow after verifying their identity.

#### `page-change`

The current page in the Inquiry flow changed. This is primarily used with the [Inlined React](/inlined-flow) flow to dynamically resize the widget based on the contents of the current page.

**`metadata.name`** `string`

Name (unique identifier) of the next step.

---

**`metadata.nameDisplay`** `string`

Descriptive name of the next step as seen and configured in Flow Editor.

---

**`metadata.path`** `string`

String representing the step type.

---

**`metadata.pageHeight`** `number`

Height in pixels of the content on the current page.

---

#### `step-transitioning`

Fired when the user transitions between steps in the inquiry flow. Requires [`eventsAllowlist`](/embedded-flow-parameters#other-parameters). Available in SDK `5.8.0` and above.

**`metadata.fromStepName`** `string`

The unique identifier of the step the user is leaving.

---

**`metadata.fromStepDisplayName`** `string`

The display name of the step the user is leaving.

---

**`metadata.direction`** `string`

The direction of the transition (e.g. `'forward'`).

---

#### `click`

Fired when the user clicks on an interactive element in the inquiry flow. Requires [`eventsAllowlist`](/embedded-flow-parameters#other-parameters). Available in SDK `5.8.0` and above.

**`metadata.componentType`** `string`

The type of component that was clicked (e.g. `'button_submit'`, `'navbar_back'`, `'id_class_select'`).

---

**`metadata.label`** `string | null`

The visible label of the clicked element, if applicable.

---

**`metadata.stepName`** `string`

The unique identifier of the current step.

---

**`metadata.value`** `string`

A value associated with the click, if applicable. For example, `'dl'` for a driver's license selection or `'autocapture'` for a camera option.

---

#### `form-update`

Fired when a form field value changes in the inquiry flow. Requires [`eventsAllowlist`](/embedded-flow-parameters#other-parameters). Available in SDK `5.8.0` and above.

**`metadata.fieldName`** `string`

The unique identifier of the form field.

---

**`metadata.interactionType`** `'focus' | 'blur' | 'change'`

The type of interaction that triggered the event.

---

**`metadata.validationOutcome`** `string`

The validation result after the interaction (e.g. `'valid'`).

---

**`metadata.validationError`** `string | null`

The validation error message, if any.

---

**`metadata.value`** `string`

*Optional.* The current value of the field. Only included for fields on the analytics allowlist configured on your inquiry template. Contact [Persona support](https://support.withpersona.com) to configure which field keys are forwarded. See [Forwarding raw field values](/embedded-flow-google-tag-manager#forwarding-raw-field-values).

---