Assetera Docs
Distribution partners

KYC integration (MetaKYC SDK)

Embed Assetera's KYC flow with the @asseteragmbh/metakyc React SDK. Get npm access, mint a session token on your server, then render the workflow.

Assetera's identity verification runs through the MetaKYC SDK, published as @asseteragmbh/metakyc. You embed it in three moves: get npm access, mint a short-lived session token on your server, then render the workflow in the browser with that token.

For a tied agent, Assetera provisions the client ID, API key, secret key and allowed workflow keys after the partner tenant is approved. The customer signs up with Assetera first. Use that signed-in customer's Assetera Identity sub as externalRefId for every session.

Ignore examples/nextjs in the SDK repo

That sample is stale: it imports the wrong package name (@metakyc/sdk) and puts an API key in the browser. Follow this page and the SDK README, not that example.

The flow

Integrate

Get npm access

The SDK is a scoped package under the @asseteragmbh organisation. Installing it needs an npm access token issued by Assetera. Treat it like a password: never commit it, inject it from a secrets manager.

Add a scope-specific auth entry so the token is used only for @asseteragmbh/* packages:

.npmrc (safe to commit: the value is a placeholder)
@asseteragmbh:_authToken=${ASSETERA_NPM_TOKEN}

npm expands ${ASSETERA_NPM_TOKEN} from the environment at install time, so the real token never enters the repository. Export it locally, and add it as a CI secret for builds.

export ASSETERA_NPM_TOKEN=npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
npm info @asseteragmbh/metakyc   # verify: prints version and dist-tags

Install

npm install @asseteragmbh/metakyc

Mint a session token on your server

Call MetaKYCSession.createToken from your backend. Secrets stay server side. The browser receives only the accessToken and applicantId.

app/api/kyc/session/route.ts (server)
import { MetaKYCSession } from '@asseteragmbh/metakyc';

export async function POST(req: Request) {
  const identitySession = await getValidatedAsseteraSession(req);
  if (!identitySession?.claims.sub) {
    return Response.json({ error: 'not signed in' }, { status: 401 });
  }

  const sdkSession = await MetaKYCSession.createToken({
    baseUrl:       process.env.METAKYC_BASE_URL!,
    clientId:      process.env.METAKYC_CLIENT_ID!,   // or tenantId
    apiKey:        process.env.METAKYC_API_KEY!,     // server only
    secretKey:     process.env.METAKYC_SECRET_KEY!,  // server only
    externalRefId: identitySession.claims.sub,       // Assetera Identity sub, server side
    workflowKey:   'INDIVIDUAL_FULL_KYC',
    email:         identitySession.claims.email,
    isCompany:     false,                            // true renders the KYB form
    applicant: {                                     // optional pre-fill
      firstName:   identitySession.claims.given_name,
      lastName:    identitySession.claims.family_name,
    },
  });

  return Response.json({
    accessToken:      sdkSession.accessToken,
    applicantId:      sdkSession.applicantId,
    expiresInSeconds: sdkSession.expiresInSeconds,
  });
}

getValidatedAsseteraSession stands for your BFF's server-side session lookup. Do not replace it with a user ID accepted from the request body.

Render the workflow

Request the token from your own route, then wrap the host page in MetaKYCProvider. The MetaKYC component picks the right screen from the token: create-applicant form, current step, or status display.

app/kyc/page.tsx (client)
'use client';
import { useEffect, useState } from 'react';
import { MetaKYC, MetaKYCProvider } from '@asseteragmbh/metakyc';

export function KycPage() {
  const [accessToken, setAccessToken] = useState<string>();

  useEffect(() => {
    fetch('/api/kyc/session', { method: 'POST' })
      .then((response) => {
        if (!response.ok) throw new Error('Unable to start verification');
        return response.json();
      })
      .then((session) => setAccessToken(session.accessToken));
  }, []);

  if (!accessToken) return <p>Opening verification...</p>;

  return (
    <MetaKYCProvider
      config={{
        getAccessToken: () => accessToken,
        baseUrl:  process.env.NEXT_PUBLIC_METAKYC_BASE_URL!,
        clientId: process.env.NEXT_PUBLIC_METAKYC_CLIENT_ID!,
        locale:   'en',
        showLanguagePicker: true,
      }}
    >
      <MetaKYC
        onApplicantCreated={(id) => {/* keep the applicant id for status lookups */}}
        onComplete={() => {/* refresh status from your backend, then route on */}}
        onError={() => {/* show a retry action */}}
      />
    </MetaKYCProvider>
  );
}

Read the result from your backend

The browser callback is a hint, not proof. A review may still be running when the embedded flow ends. Take the result from webhooks, and read applicant progress when you need status on demand.

Provider config

baseUrl plus one of getAccessToken or apiKey is the minimum. Everything else is optional.

FieldTypeDefaultDescription
baseUrlstringnoneMetaKYC API base URL, from your tenant handover.
getAccessToken() => stringnoneReturns the session token. Required unless apiKey is set.
apiKeystringnoneDirect API key. Testing only, never ship it to a browser.
tenantId / clientIdnumber / stringnoneSets the tenant scope header.
endpoints.pattern'host-controller', 'application-service', 'custom'host-controllerURL pattern for backend calls.
localestring'en'Initial UI locale.
showLanguagePickerbooleanfalseShow the language dropdown.
showVersionbooleanfalseShow the SDK version badge.
debug / logLevelboolean / stringoffRequest trace logging. Use it while integrating.
applicantIdnumbernoneResume a previous session by ID.
applicantFormobjectnoneField visibility and pre-fill (see below).
identityProvidersobjectnonePer-provider settings (Sumsub, Sardine, Onfido).
themeobjectfrom backendOverride the theme, otherwise loaded from tenant settings. See Styling the KYC SDK.
configVersionstringlive configLoad a saved configuration snapshot (see below).

applicantForm

FieldDescription
applicantType'individual' (default) or 'company'.
visibleFieldsWhitelist of fields to render. Omitted, the admin-panel defaults apply.
workflowKeyDefault workflow key, overridden per session by the token.
externalRefIdExternal ID to send when it is not in visibleFields.
initialValuesPre-fill and lock values (read-only in the UI, still submitted).
hiddenValuesSubmitted but never shown. Unknown keys become applicant additional data.
fieldLabelMode'label' (default) or 'placeholder'.

configVersion

Your tenant configuration (theme, applicant-form layout, language allowlist) can be saved as named versions in the admin panel. By default the SDK loads whatever is live. Pass configVersion to load a named snapshot instead.

<MetaKYCProvider
  config={{
    baseUrl:        process.env.NEXT_PUBLIC_METAKYC_BASE_URL!,
    clientId:       process.env.NEXT_PUBLIC_METAKYC_CLIENT_ID!,
    getAccessToken: getToken,
    // Load the saved snapshot called "v2-dark-theme".
    // Omit this line and the live configuration is used.
    configVersion:  'v2-dark-theme',
  }}
>
ValueWhat happens
OmittedThe live configuration is served. This is the default and what most integrations want.
A known version nameThat snapshot is served, for every user of this SDK instance.
An unknown nameFalls back to the live configuration. A typo degrades quietly instead of failing the flow.

Typical uses: A/B testing two layouts, or pinning a staging site to a draft configuration while production stays on the live one.

Component props

PropTypeDescription
isCompanybooleanForce the company (KYB) form. Otherwise read from the token.
onApplicantCreated(id: number) => voidFires after the applicant record is created.
onComplete(result: WorkflowResultType) => voidFires when the workflow finishes.
onError(error: Error) => voidFires on an unrecoverable error.
classNamestringExtra CSS classes on the SDK shell.

To build your own UI instead of the drop-in, use the hooks: useKycWorkflow, useQuestionnaire, useRiskScoring, useUploadDocument, useAppropriatenessTest, useOverview and useIdentityVerification. They share the same provider and session token.

Get these right

  • Secrets are server only. apiKey and secretKey never reach the browser.
  • externalRefId is required and scopes the session to one user. For tied agents it is the customer's Assetera Identity sub, read from the validated BFF session. A mismatch is rejected.
  • workflowKey selects the flow, and isCompany switches between the KYC and KYB forms.
  • KYC status is separate from the login account. Trading is gated on completed KYC, and Assetera is the responsible party for KYC and AML (see Tenancy and responsibility).

Use your tenant handover

Concrete values (baseUrl, clientId or tenantId, the available workflowKeys, the endpoints.pattern) are issued with your tenant setup. Pin the SDK version supplied in that handover and upgrade through a tested dependency change.

On this page