Flonk
Flonk Docs

Changelog

Release notes for the Flonk KYC platform and the @flonkid/kyc SDK.

Last updated: 8/25/2026
5 min read

Notable changes to the Flonk KYC platform and the official SDK. Dates are in UTC. The SDK follows semantic versioning.


Platform

2026-06 — API versioning + idempotency

  • Date-pinned API versioning. Requests may send a Flonk-Version header; the server resolves it (absent/unknown → current baseline), echoes it back, and the API is additive-only within a version. Lays the rails for future breaking changes without breaking deployed clients. No behaviour change for existing callers.
  • Idempotent session creation. POST /v1/sessions accepts an Idempotency-Key header — a retry returns the original session instead of a duplicate (concurrent in-flight retry → 409). Keys scoped per project, 24h.

2026-06 — Replay-protected webhook signatures

  • Webhooks now include a timestamped X-Signature header (t=<unix>, v1=<hex>) in addition to the existing X-Signature-256 (sha256=). The timestamp is signed, so a captured request is rejected once it's outside the skew window (5 min) — real replay protection. X-Signature-256 is unchanged and stays for backward compatibility; existing integrations keep working. Verify either with the SDK's constructEvent, and dedupe retries by event.id.

2026-05 — Faster widget theming

  • Design tokens are now cached in Redis per project. The GET /public/design-tokens endpoint — the widget's first, blocking request — previously ran a database join on every widget load. It now serves from a Redis cache (5-minute TTL) keyed by project, with the cache invalidated automatically whenever a project's branding color changes. The widget's brand color resolves from a sub-millisecond cache read on the hot path.

SDK

The browser/Node package published as @flonkid/kyc.

2.0.0

Breaking — read this before upgrading.

  • verify.flonk.id is the default widget origin. An integration that never passed widgetUrl was rendering the previous widget and now renders widget-v2: different capture UX, auto-capture and face detection. No exported signature moved, which is exactly why this is a major — it would otherwise arrive silently on an npm update. Pin new FlonkKYC({ widgetUrl: 'https://widget.flonk.id' }) to stay where you were. If you already set widgetUrl, nothing changes for you.
  • The CSP block changed, and it is not a rename. frame-src names verify.flonk.id; script-src names api.flonk.id only — the widget origin hands the host page no script and is removed from that directive, not renamed; img-src is gone entirely because both loaders draw the spinner with createElementNS and fetch no image. Copy the block from the README rather than editing the old one in place.
  • Permissions-Policy is documented for the first time. It is the most common cause of a camera that never starts on an otherwise correct integration: the SDK delegates camera to the frame, but allow can only hand down what the host page holds. Not new behaviour — it worked this way on the previous widget too — but it now appears where a 2.0 upgrader will read it.

Added

  • A <script> tag build. GET /v1/public/widget-v2.js registers window.KYCWidget and reads its configuration from data-* attributes, so a site with no build step can integrate. data-email is required — the person being verified has to be named.
  • A live inline mount. mountInline() runs a real verification inside your own layout — same session flows, same wire — with no overlay, no scroll lock and no z-index takeover. Not to be confused with embed(), which is a themed mock for dashboards.
  • redirect() sends the whole page to the widget, creating the session first so a failing backend surfaces through onError before the page is left. For in-app browsers and WebViews, where a first-party origin is far likelier to be granted the camera than an iframe.

Fixed

  • The modal was an invisible sheet over a page that looked live. Nothing painted a backdrop once the loader was destroyed at reveal, so the host page sat bright and apparently interactive under a sheet that swallowed every click. There is now one scrim, alive from open to close, carrying the dialog semantics and the focus ring — and it owns the body-scroll lock, which two places used to fight over and leave the host page permanently fixed after close.
  • The loader painted Flonk's own brand colour on the merchant's page before the project's colour resolved, and a later fix stopped an unknown colour from overwriting the one the server had already baked in.
  • Two of four subpaths shipped no type declarations (/server and /types — a race in the build's clean step), and require.types pointed at ESM declarations, so a CJS consumer under node16 hit TS1479.
  • The server client refused the one key shape that enables test mode.
  • The desktop modal hid 136 px of every content-heavy screen.
  • prewarm at level intent behaved like eager when given no trigger — it loaded the full widget bundle on every page view instead of degrading to connect. The script-tag path cannot supply a trigger at all, so every page that asked for the middle level was silently getting the heaviest one. Its design-token prefetch was also requesting a URL that had 404'd since it was written, so that half of prewarm never warmed anything.

1.9.4

  • Faster widget start — dropped a redundant token round-trip. In the serverUrl and sessionId+embedToken flows the SDK passed the embed token to the widget iframe only as token, not embedToken. The widget keys its fast path on the embedToken param, so without it it fell back to a deprecated server exchange — an extra POST /public/session/:id/token before verification could start. The SDK now sends embedToken too (as the classic widget.js loader already did), so the widget initializes directly and that request is gone. No integration changes needed.

1.9.3

  • New: @flonkid/kyc/core — a React-free entry. The default @flonkid/kyc entry re-exports the FlonkKYCWidget React component, so it imports react. In a non-React project (Vue, Angular, Svelte, vanilla) that has no react installed, that broke the production bundler build. Import the FlonkKYC class from @flonkid/kyc/core instead — same class, zero React, zero config. Fully additive — React users keep importing from @flonkid/kyc unchanged. See Frontend SDK → Other frameworks.

1.9.2

  • Fixed: desktop→mobile QR didn't render in the serverUrl flow. The SDK now forwards qrCodeUrl to the widget. Your create-session endpoint should respond with { sessionId, embedToken, qrCodeUrl } (the sessionId + embedToken flow can also pass qrCodeUrl in init). Previously only the classic widget.js loader passed it.
  • Docs: clarified that the serverUrl endpoint must return { sessionId, embedToken } (the API's id mapped to sessionId) — returning the raw session object silently broke the widget at the welcome step.

1.9.1

  • Observability — onDiagnostic hook. The SDK now emits a structured diagnostic at every degradation/branch point (loader fallback, blocked loader script, prewarm skipped, ready-timeout reveal, protocol mismatch) instead of failing silently. Pass onDiagnostic, or set window.__FLONK_DEBUG__ = true to mirror events to the console. Zero-cost when unused. See Frontend SDK → Debugging.
  • Versioned SDK ⇄ iframe contract. The wire between the SDK and the widget iframe is now versioned (PROTOCOL_VERSION) separately from the package version and is additive-only, so any SDK build stays compatible with any iframe build. A PROTOCOL_VERSION_MISMATCH diagnostic surfaces a stale-cached widget.
  • Webhooks — added a replay-protected signature. Flonk now also sends a timestamped X-Signature (t=,v1=) header alongside X-Signature-256 (both HMAC-SHA256 with your secret). The signed timestamp adds replay protection (old requests rejected outside the skew window); X-Signature-256 stays fully valid for authenticity + integrity. constructEvent verifies either. Signature comparison is now branchless constant-time. Dedupe retries by event.id in your own store — see Webhooks → Idempotency.

1.9.0

  • Self-prewarming. New static FlonkKYC.prewarm({ level }) — preconnect + idle asset prefetch, with level:'intent' (warm on hover/focus/in-view) and level:'eager' (pre-mount a hidden iframe so the full bundle loads before the click). The classic widget.js loader self-prewarms on load (data-prewarm="eager" for the eager level). Never pre-creates a session; SSR-safe. See Frontend SDK → Performance.
  • Server-hosted loader. The branded loading screen is now served from the API (/v1/public/loader.js) so it can be restyled and fixed for all integrations without a client SDK bump. The SDK falls back to its bundled loader if the script is blocked (CSP/CORP/offline).
  • Fixed: loader could hang on "Initialisierung…". The widget signalled READY from a requestAnimationFrame callback, which the browser pauses for a hidden/backgrounded iframe — deadlocking the reveal for some users. READY is now driven by timers only, and the SDK reveals on a safety timeout even if READY is missing, so a permanent stuck loader can't happen.

1.7.0

  • Fixed: React is not defined under SSR / Turbopack. The build emitted bare React.createElement(...) calls without importing React, which crashed in environments without a global React (Next.js App Router, Turbopack runtime). The bundle now compiles JSX with React's automatic runtime (react/jsx-runtime), so no globalThis.React shim is needed. react and react/jsx-runtime remain peer/external — your app's React is used.
  • Improved: instant branding now works in the sessionId + embedToken flow. Previously publishableKey only sped up branding in the serverUrl flow; in the manual-session flow it was ignored and branding waited on the session request. Now, when publishableKey is supplied, the SDK resolves the brand color from the key in parallel with the session (raced against an 800 ms budget, color updated in place when full tokens arrive) — matching the serverUrl fast path.
  • Docs: WidgetInitConfig.publishableKey JSDoc updated to describe instant branding in both flows.

1.5.1

  • Fixed branded loader not appearing after the serverUrl session resolved.

1.5.0

  • Added the requestHeaders prop to send authentication headers (e.g. a JWT) with the serverUrl POST request.
  • Introduced publishableKey-driven instant branded loader for the serverUrl flow.

1.4.x

  • 1.4.2 — Hardened against double initialization in React StrictMode using a generation counter.
  • 1.4.1 — Surface server error bodies; show errors in the loader for the serverUrl flow instead of failing silently.
  • 1.4.0 — Replaced loose any types with precise types across the SDK; aligned onSuccess to emit a typed VerificationResult.

See the Frontend SDK guide for current usage.

On this page

Need Help?

Get in touch with our team for technical support.

Contact Support