Skip to content

SDK embed or hosted link: choosing how to integrate verification

Embed the SDK when verification happens inside your product; send a per-applicant hosted link when it happens outside it. Either way, run a published workflow so the choice is about delivery, not about the flow.

Charles Archibong

, Co-founder

· 6 min read

Headline "SDK embed or hosted link" beside an illustration of one starting point branching into two paths, on a deep indigo gradient.

Key takeaways

  • Choose the surface by where verification happens: inside your product (embed) or outside it (link).
  • A per-applicant session link carries your user reference; a shared hosted link cannot tell you who opened it.
  • Build the flow as a published workflow first, so embed and link run the same checks.
  • Hosted links in a WebView report progress events, but NFC chip reading and native liveness need the native SDKs.

Embed the SDK when verification happens inside your product, at a moment your user reaches in their own session. Send a hosted link when verification happens outside it: a loan officer chasing documents, a merchant being onboarded by email, a customer asked to re-verify months after signup. That is the whole decision rule, and most teams end up needing both.

What makes the choice cheap is building the flow once as a published workflow. An embed mounts it by id, a link runs the same published snapshot, and the checks, branding and decision rules are identical. You are choosing a delivery surface, not a second verification flow to maintain.

What actually differs between the two

Both routes end in the same place. The applicant completes the capture steps, the platform processes the verification asynchronously, and your backend learns the result from a webhook or a status poll. The differences are in who starts the flow and what your code controls.

Question

SDK embed

Hosted link

Where does the applicant start?

Inside your web or mobile app

A URL opened from email, SMS, a QR code or a WebView

What do you install?

A client SDK and a publishable key

Nothing on the client; a secret key on your backend to mint per-applicant links

Who is the applicant?

Your signed-in user, passed as userId

Named by externalUserId on a per-applicant session, or unknown on a shared link

What do you hear in real time?

SDK callbacks such as onSubmit, onStepChange and onError

Hosted link events when the page runs in a WebView or iframe; otherwise only your webhook

Device capabilities

Full, including NFC chip reading and on-device liveness on the native SDKs

Browser capabilities only

Changing the flow

Re-publish the workflow

Re-publish the workflow

The last row is the one people get wrong. With a workflow, neither route needs a code change to alter the flow. Without one, an embed means spelling out country, ID types and step toggles as props, and every change becomes a release.

When the SDK embed is the right choice

Embed when verification is a step in a journey you control and the user is already signed in. A wallet app that verifies a user before their first transfer, or a marketplace that verifies sellers before their first listing, both fit.

You get three things a link cannot give you.

Callbacks in your own code. The SDK calls onSubmit with a verificationId the moment the verification is created, so you can move the user to a pending screen without waiting on a webhook. onError reports technical failures (network, camera permission, a disabled ID type) with a stable code.

Native device capability. The React Native and Flutter SDKs read passport chips over NFC and run liveness on the device. Browsers cannot talk to a passport chip, so the web SDK only shows the chip screen in the dashboard builder preview.

Identity you already hold. You pass userId and userData from your session, so the verification is tied to a known account and the name you hold is compared with the document.

A minimal web embed, taken from the SDK documentation:

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

The costs are real but modest. You add a dependency, you keep it at or above the minimum versions that understand workflowId (web 2.2.0, React Native 2.1.0, Flutter 2.2.0 or later), and on React Native you need a custom development build because the SDK ships native code.

Send a link when the person you need to verify is not in your app at the moment you need them. A few common shapes:

  • A business lender collecting KYC from a director who has never logged in to anything.

  • An operations team re-verifying a customer whose document has expired.

  • A platform with no mobile app, verifying users who arrive by email.

  • A partner channel where you cannot ship code at all.

There are two kinds of link, and choosing between them matters more than choosing link versus embed.

When you know who should verify, mint a session from your backend with POST /api/kyc/sessions and a secret key:

curl "https://trust.myaza.app/api/kyc/sessions" \
  -H "Authorization: Bearer $MYAZA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workflowId": "wf_AbC123dEf456",
    "externalUserId": "user_42",
    "userData": { "firstName": "John", "lastName": "Doe" }
  }'

The response carries a url that belongs to that one applicant. It is single use, traceable to your externalUserId, and spent once submitted, so forwarding it to a second person does not work. You can pass email to have the link sent on your behalf, or deliver it yourself.

Desktop visitors on a hosted page are offered a "continue on your phone" QR code for the camera steps, so a link emailed to someone at a laptop still reaches a camera. (The web SDK has the same hand-off on desktop, controlled by its deviceHandoff option.)

Two details worth planning around. The link stops accepting a new start at expiresAt: 24 hours for individual flows and 7 days for business flows, unless the workflow sets its own session lifetime. And the configuration the applicant walks is the published snapshot frozen when you minted the link, so a re-publish never changes a link already sitting in someone's inbox.

Every published workflow can also be turned on as one public URL that many people open, each visit starting its own verification. It suits a campaign or a QR code on a counter, where you genuinely do not know who will arrive. The trade-off is attribution: the link carries no reference to a user in your system, so you have to reconcile results some other way. Regenerating the link from the workflow's page kills the old URL at once, which is your recourse if it circulates further than intended. If you know the person, use a session.

Some teams want the page hosted by us but opened inside their own mobile app, usually to avoid a native dependency. That works. Open the session url in react-native-webview, webview_flutter or an iframe, and the page reports its progress to the host as hosted link events: ready, started, step, submitted, completed, error and closed.

<WebView
  source={{ uri: session.url }}
  onMessage={(event) => {
    const message = JSON.parse(event.nativeEvent.data);
    if (message.source !== 'myaza-kyc') return;
    if (message.type === 'submitted') {
      navigation.replace('VerificationPending', { id: message.verificationId });
    }
  }}
/>

Know the limits before you choose it. NFC chip reading, background presence checks and native liveness are not available inside a WebView. The events carry ids, step names and error codes, never anything the applicant typed or captured, and they are a courtesy to your interface, not the record. The verdict still comes from your webhook or a status poll.

In an iframe, append your page's origin as an origin query parameter. The page posts events only when the browser confirms your page really is the embedder, and posts nothing otherwise.

A worked decision

Consider a hypothetical consumer lender with a mobile app and a field sales team. New customers verify inside the app before their first loan, so the app embeds the React Native SDK with a published workflow that includes the passport chip read. Field agents onboard traders in markets who have not installed the app yet, so the backend mints a session per trader with the trader's lead id as externalUserId, and the agent sends the link by SMS. Customers whose ID later expires get a fresh session link from operations.

One workflow could serve all three, or the lender could keep a lighter workflow for the field links. Either way, every result lands on the same webhook handler, keyed by verificationId and carrying the lender's own externalUserId.

The decision rule

  1. Build the flow as a published workflow before choosing a surface.

  2. If the user is signed in to your product at the moment of verification, embed the SDK. Use a native SDK if you need NFC chip reading.

  3. If verification happens outside your product and you know the person, mint a per-applicant session from your backend.

  4. Use a shared hosted link only when you cannot know who will open it, and plan how you will attribute results.

  5. If you want a hosted page inside your app, open the session URL in a WebView and listen for hosted link events, accepting the loss of chip reading and native liveness.

  6. Whatever you choose, treat the webhook as the source of truth and the client-side signal as a hint.

Sources

Charles Archibong

About the author

Charles Archibong

Co-founder

Charles Archibong co-founded Myaza Trust. He writes about identity verification, financial technology, and the practical work of building trusted digital services.

Build your product.We'll handle the rest.

Identity and compliance, end to end, built to global standards, priced for founders.

SDK embed or hosted link for identity verification · Myaza Trust