Web SDK (React)
Build with

The Web SDK is a drop-in React component that renders the full verification UI — ID selection, document scan, and active liveness — and calls the verification API for you. It uses a publishable (pk_) key and detects the environment automatically from the key prefix (pk_test_* → sandbox, pk_live_* → production). For shared concepts — supported countries, branding, results, and errors — see Client SDKs.

Install
Build with

shell
pnpm add @myazahq/kyc-sdk-react
shell
yarn add @myazahq/kyc-sdk-react
shell
npm install @myazahq/kyc-sdk-react

Usage
Build with

<MyazaKYC /> renders a "Verify Identity" button plus the full modal flow. The trigger is a real <button> — pass children to relabel it, className to restyle it, or any other button attribute (disabled, type, aria-*, …). See Customizing the trigger button. Import the bundled stylesheet once, anywhere in your app.

tsx
"use client";

import { MyazaKYC } from "@myazahq/kyc-sdk-react";
import "@myazahq/kyc-sdk-react/styles.css";

export default function VerifyButton() {
  return (
    <MyazaKYC
      apiKey="pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      country="NG"
      idTypes={["bvn", "drivers-license", "passport"]}
      userData={{ firstName: "Jane", lastName: "Doe" }}
      enableSelfie
      enableDocumentCapture
      enableLiveness
      showThemeToggle
      appearance={{ primaryColor: "#5645F5", companyName: "Myaza", logo: "default", theme: "light" }}
      consent={{ title: "Welcome, {firstName}", description: "A quick check to confirm it's really you." }}
      success={{ title: "You're all set, {firstName}!", description: "We'll email you once your verification is reviewed." }}
      metadata={{ requestId: "order_1001", userId: "user_42" }}
      onSubmit={(submission) => {
        // The verification was created; status is always 'pending'.
        // Reconcile the final result on your backend via webhook or a secret-key
        // GET /verifications/:id call (never from the client).
        console.log("submitted", submission.verificationId);
      }}
      onError={(err) => console.error(err)}
      onClose={() => console.log("closed")}
    />
  );
}

Props
Build with

PropTypeDefaultDescription
apiKeystringRequired. Sent as Authorization: Bearer. The environment is derived from the key prefix (pk_test_* → sandbox, pk_live_* → production); an unrecognized prefix throws.
workflowIdstringRun a published workflow (wf_…) built in the dashboard. The SDK fetches its configuration and uses it as the source of truth — workflow config wins over overlapping props. Makes country optional. See Configure with a workflow.
country'NG' | 'GH' | 'KE' | 'ZA' | 'CI' | stringRequired unless workflowId is set. Country whose ID types are offered. Any ISO-2 country works — the org's grants are enforced server-side.
livenessMode'gestures' | 'flash' | 'both''gestures'How the liveness step proves presence: randomized gestures, the screen-flash sequence, or both. Usually set by a workflow.
deviceIntelligencebooleantrueCollect device + IP fraud signals (Device Intelligence).
countriesArray<{ country, idTypes? }>Multi-region. List more than one country and the flow opens with a country-select step; the picked country's idTypes win. Usually set by a workflow.
idTypesIdType[]all enabled for orgSubset of ID types to offer; must be valid for country.
userData{ firstName?, lastName?, dateOfBirth? }Pre-fills the user's details; fields provided here aren't asked again.
enableSelfiebooleantrueCapture a selfie during liveness.
enableDocumentCapturebooleantrueEnable the document-scan step for document IDs.
allowDocumentUploadbooleantrueAllow picking a document photo from the device (gallery / drag-and-drop) instead of the camera. false hides every "upload instead" affordance — except on the camera-permission-denied screen, where it stays as an escape hatch.
enableLivenessbooleantrueRun the liveness challenge step. The server can still disable it per ID type.
voiceGuidanceboolean | { enabled?, language? }trueSpoken liveness instructions (accessibility, TTS output — no microphone). false mutes it; { language: 'fr-FR' } sets the voice language.
showThemeTogglebooleantrueShow a light/dark toggle inside the modal header. Set false to hide it — the flow then stays on appearance.theme and the user can't switch it.
fullScreenbooleanfalseForce the flow to render full screen on every device (desktop drops the centered modal; the expand/collapse control is hidden).
disableClosebooleanfalseHide the close (X) button and block all user dismissal (backdrop click, Escape, mobile swipe-down). The flow can then only be closed programmatically via the useMyazaKYC() hook's close(). The terminal "Submitted" step is non-dismissible regardless.
deviceHandoffbooleantrueOn desktop, show a "continue on your phone" screen (QR code + copyable link) before the flow starts — useful when the computer has no webcam. The user can still continue on the current device; when they finish on their phone, the desktop completes automatically and fires onSubmit. Set false to disable. No effect on mobile/touch devices.
appearanceKYCAppearancebrand defaultsBrand & theme the modal — colors, logo, light/dark. See Branding & theming.
consentKYCConsentContentbuilt-in copyOverride the consent/welcome screen title and description. See Consent screen copy.
successKYCSuccessContentbuilt-in copyOverride the success/submitted screen title and description. See Success screen copy.
metadataRecord<string, string>Forwarded with the verify request (include your requestId).
onStart() => voidCalled when the flow opens.
onStepChange(step: KYCStep) => voidCalled on each step transition.
onSubmit(submission: KYCSubmission) => voidCalled when the server accepts the verification. status is always 'pending'.
onClose() => voidCalled when the user closes the flow.
onError(error: KYCError) => voidCalled for technical errors only — receives a typed KYCError (a real Error with a code). Verification outcomes never come through here. See Errors.
childrenReactNodeVerify IdentityTrigger button label/content. Defaults to Verify with {companyName} when companyName is set, else Verify Identity.
classNamestringTrigger button classes. Merged via tailwind-merge, so your classes override the built-in styling.
other button attrsButtonHTMLAttributesAny standard <button> attribute (disabled, type, aria-*, style, …) is forwarded. onClick is reserved by the SDK.

Configure with a workflow
Build with

Instead of spelling out country, idTypes, and every step toggle in code, you can build the flow once in the dashboard as a workflow and mount it by id:

tsx
<MyazaKYC
  apiKey="pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  workflowId="wf_AbC123dEf456"
  userId="user_42"
  metadata={{ requestId: "order_1001" }}
  onSubmit={(s) => console.log(s.verificationId)}
/>

The workflow's configuration wins over any overlapping props, so the country, ID types, capture steps, branding, and copy all come from the dashboard. Only runtime datauserId, userData, metadata, and your callbacks — stays in code.

A workflow is also the recommended way to turn on the extra capture steps — contact OTP (emailVerification / phoneVerification), proof of address (proofOfAddress), NFC chip (nfc), questionnaire, and business (KYB) verification (subjectType: 'business' + business) — and to attach server-side decisioning. These are accepted as SDK props too, but configuring them on the workflow means you compose the checks once and change them by re-publishing — no redeploy. See Workflows.

Customizing the trigger button
Build with

<MyazaKYC /> renders a real <button>. In addition to the config props above it accepts standard button attributes (the props type is exported as MyazaKYCProps), so you can treat it like any other button:

tsx
<MyazaKYC
  {...config}
  className="w-full rounded-full bg-black px-6 text-white"
  disabled={!ready}
>
  Start verification
</MyazaKYC>
  • children sets the label (falls back to Verify with {companyName} / Verify Identity).
  • className is merged through tailwind-merge, so your classes win over the defaults.
  • style is merged on top of the SDK's injected theme variables, so theming still applies.
  • onClick is owned by the SDK (it opens the modal) and can't be overridden. For a fully custom trigger element, use the useMyazaKYC() hook below.

Programmatic control
Build with

For a custom trigger instead of the built-in button, wrap your tree in KYCProvider and drive the flow with the useMyazaKYC() hook:

tsx
import { KYCProvider, useMyazaKYC } from "@myazahq/kyc-sdk-react";

function Trigger() {
  const { open, close, isOpen, currentStep } = useMyazaKYC({
    apiKey: "pk_test_…",
    country: "NG",
    onSubmit: (s) => console.log(s.verificationId),
  });

  return <button onClick={open} disabled={isOpen}>Verify ({currentStep ?? "idle"})</button>;
}

export default () => (
  <KYCProvider>
    <Trigger />
  </KYCProvider>
);

The flow advances through KYCStep values, reported via onStepChange / currentStep. The core path is consentid-type → (id-input for number-only IDs, or document-capture) → livenesssubmitted. Steps enabled by a workflow slot in automatically — email-verification / phone-verification (right after consent), country-select (multi-region), proof-of-address, questionnaire, and the KYB steps (business-details, business-key-people, business-documents, applicant-role).