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:
@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-tagsInstall
npm install @asseteragmbh/metakycMint a session token on your server
Call MetaKYCSession.createToken from your backend. Secrets stay server side. The browser receives only
the accessToken and applicantId.
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.
'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.
| Field | Type | Default | Description |
|---|---|---|---|
baseUrl | string | none | MetaKYC API base URL, from your tenant handover. |
getAccessToken | () => string | none | Returns the session token. Required unless apiKey is set. |
apiKey | string | none | Direct API key. Testing only, never ship it to a browser. |
tenantId / clientId | number / string | none | Sets the tenant scope header. |
endpoints.pattern | 'host-controller', 'application-service', 'custom' | host-controller | URL pattern for backend calls. |
locale | string | 'en' | Initial UI locale. |
showLanguagePicker | boolean | false | Show the language dropdown. |
showVersion | boolean | false | Show the SDK version badge. |
debug / logLevel | boolean / string | off | Request trace logging. Use it while integrating. |
applicantId | number | none | Resume a previous session by ID. |
applicantForm | object | none | Field visibility and pre-fill (see below). |
identityProviders | object | none | Per-provider settings (Sumsub, Sardine, Onfido). |
theme | object | from backend | Override the theme, otherwise loaded from tenant settings. See Styling the KYC SDK. |
configVersion | string | live config | Load a saved configuration snapshot (see below). |
applicantForm
| Field | Description |
|---|---|
applicantType | 'individual' (default) or 'company'. |
visibleFields | Whitelist of fields to render. Omitted, the admin-panel defaults apply. |
workflowKey | Default workflow key, overridden per session by the token. |
externalRefId | External ID to send when it is not in visibleFields. |
initialValues | Pre-fill and lock values (read-only in the UI, still submitted). |
hiddenValues | Submitted 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',
}}
>| Value | What happens |
|---|---|
| Omitted | The live configuration is served. This is the default and what most integrations want. |
| A known version name | That snapshot is served, for every user of this SDK instance. |
| An unknown name | Falls 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
| Prop | Type | Description |
|---|---|---|
isCompany | boolean | Force the company (KYB) form. Otherwise read from the token. |
onApplicantCreated | (id: number) => void | Fires after the applicant record is created. |
onComplete | (result: WorkflowResultType) => void | Fires when the workflow finishes. |
onError | (error: Error) => void | Fires on an unrecoverable error. |
className | string | Extra 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.
apiKeyandsecretKeynever reach the browser. externalRefIdis required and scopes the session to one user. For tied agents it is the customer's Assetera Identitysub, read from the validated BFF session. A mismatch is rejected.workflowKeyselects the flow, andisCompanyswitches 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.
Tied-agent walkthrough (Next.js + BFF)
Building a Next.js app as a tied-agent tenant: an OIDC login, a server-side session holding the tokens, and a tenant-gated proxy to the Marketplace API.
Applicant status (GetApplicantProgress)
Read where a user is in the KYC workflow. The GetApplicantProgress request, its response shape, and the enum vocabulary every status field uses.