Integration guide

Add the KYI widget to your platform: one endpoint on your backend and one function call on your frontend.

1. Set up with Bluprynt

Contact your Bluprynt representative and send them every origin where the widget will be embedded, for example https://app.example.com, https://staging.example.com and http://localhost:3000. Bluprynt adds them to its CSP and CORS policies. The widget won't load anywhere else.

In return you get:

Widget shows an empty frame, or the console has a CSP error? The page's origin probably isn't on the allowlist. Scheme, host and port all have to match exactly, so http://localhost:3000 and http://localhost:5173 are different origins.

2. Install the SDK

npm install @bluprynt/kyi-widget-sdk

The package has two entry points:

3. Generate an access token on your backend

Add an endpoint that your frontend calls before opening the widget. It returns a short-lived token for the logged-in user.

import { generateToken } from '@bluprynt/kyi-widget-sdk/server'

app.post('/api/kyi/token', requireLogin, async (req, res) => {
  const accessToken = await generateToken({
    issuer: 'your-partner-id',
    secretKey: process.env.BLUPRYNT_SECRET_KEY,
    userId: req.session.user.id,
    expiresIn: 3600,
  })

  res.json({ accessToken })
})

A note on naming: issuer here is the standard JWT term for whoever signs the token, which is your platform. Put your partner ID in it. It has nothing to do with the token issuers who use the widget.

Choosing a user ID

Bluprynt identifies a user by userId alone and doesn't check it. Their KYB results, verified wallets and on-chain identity are all stored against it. Pick an ID that is:

Take the user ID from your authenticated session, never from the request. If your endpoint read something like req.body.userId, anyone could ask for a token for someone else and open that person's verification. Bluprynt can't catch this, because the token is correctly signed.

Not using Node.js?

Generate the JWT with any library for your language. Sign it with HS256 using your SECRET_KEY, and include these claims:

{
  "sub": "user-123",          // your internal user ID
  "iss": "your-partner-id",   // your partner ID (see the naming note above)
  "iat": 1766146154,          // issued at (Unix seconds)
  "exp": 1766149754           // expires at (Unix seconds)
}

For example, in Python with PyJWT:

import time, jwt

now = int(time.time())
access_token = jwt.encode(
    {"sub": user.id, "iss": "your-partner-id", "iat": now, "exp": now + 3600},
    BLUPRYNT_SECRET_KEY,
    algorithm="HS256",
)

4. Open the widget on your frontend

import { kyi } from '@bluprynt/kyi-widget-sdk'

const { accessToken } = await fetch('/api/kyi/token', { method: 'POST' })
  .then((r) => r.json())

const widget = kyi('drawer', 'kyi', accessToken, {
  onReady: () => console.log('Widget loaded'),
  onClose: () => console.log('Widget closed'),
  onError: (error) => console.error(error),
})

Generate a new token each time you open the widget rather than caching one. Tokens are short-lived on purpose.

Display mode

The widget opens as a drawer: a panel that slides in from the right. It closes when the user clicks the close button, clicks outside it, or presses Escape.

Scopes

The scope decides which screen the widget opens on.

ScopeOpensUse it for
kyiThe verification flowStarting or continuing KYB and wallet verification.
asset-listThe user's assetsReviewing assets they've verified.
wallet-listThe user's walletsReviewing wallets they've verified.

Cleaning up

The widget removes itself when the user closes it. To close it from your own code, for example when the user navigates away in a single-page app, call widget.destroy().

React example

import { useEffect, useRef } from 'react'
import { kyi, type KYIWidget } from '@bluprynt/kyi-widget-sdk'

export function VerifyButton() {
  const widgetRef = useRef<KYIWidget | null>(null)

  useEffect(() => () => widgetRef.current?.destroy(), [])

  const open = async () => {
    const { accessToken } = await fetch('/api/kyi/token', { method: 'POST' })
      .then((r) => r.json())
    widgetRef.current = kyi('drawer', 'kyi', accessToken, {
      onClose: () => { widgetRef.current = null },
    })
  }

  return <button onClick={open}>Verify your asset</button>
}

Security checklist

Want to try it before writing code? Use the playground.