Troubleshooting
Solutions for common KYC integration issues.
SDK Issues
Module Not Found
Symptom: Cannot find module '@flonkid/kyc'
Solution: Install the SDK:
bashnpm install @flonkid/kyc
SSR Error (Next.js)
Symptom: window is not defined or document is not defined
Solution: Use dynamic import to avoid SSR:
typescript// Wrong: top-level import runs on serverimport { FlonkKYC } from '@flonkid/kyc';// Correct: dynamic import runs only in browserconst startKYC = async () => {const { FlonkKYC } = await import('@flonkid/kyc');const kyc = new FlonkKYC();// ...};
serverUrl HTTPS Error
Symptom: serverUrl must use HTTPS in production
Solution: The SDK enforces HTTPS for absolute URLs. Use a relative path or HTTPS:
typescript// Relative path (OK)serverUrl: '/api/kyc/create-session'// HTTPS (OK)serverUrl: 'https://api.myapp.com/kyc/create-session'// HTTP (rejected, except localhost)serverUrl: 'http://api.myapp.com/kyc/create-session' // Error!
Widget / Loader Issues
Turn on debug logging first
Before anything else, make the SDK tell you what it's doing. Every degradation
(blocked loader script, prewarm skipped, no READY from the iframe, protocol
mismatch) is reported — silent by default, visible when you opt in:
typescript// Anywhere before the widget opens — no rebuild needed:window.__FLONK_DEBUG__ = true;// Or capture events in code:const kyc = new FlonkKYC({onDiagnostic: (e) => console.log(`[flonk:${e.code}] ${e.message}`, e.detail),});
For the classic <script src=".../widget.js"> loader, add data-debug to the
tag (or set the same global). Then reproduce and read the [flonk:*] codes.
Loader stuck / widget never appears
Symptom: the loading overlay stays up; the widget content never shows.
Diagnose: enable debug logging (above) and look for:
| Code | Cause | Fix |
|---|---|---|
READY_TIMEOUT_REVEAL | The iframe never sent READY (usually a CSP frame-src block or a hard error inside the iframe). | Allow https://widget.flonk.id in frame-src (see below); check the iframe's own console. |
LOADER_SCRIPT_BLOCKED | The branded loader script was blocked (CSP script-src, CORP, or offline). | Allow https://api.flonk.id in script-src. The SDK still works with its bundled loader. |
PROTOCOL_VERSION_MISMATCH | The SDK and the cached iframe speak different wire versions. | Hard-refresh / clear the site's cache to drop the stale widget. |
The SDK reveals the widget on a safety timeout even if READY is missing, so a
permanent "stuck loader" should not happen — if it does, it's almost always a
frame-src CSP block stopping the iframe from loading at all.
Content Security Policy (CSP) blocks the widget
Symptom: the iframe or loader script is blocked; console shows a CSP
violation or ERR_BLOCKED_BY_RESPONSE.
Solution: if your site sends a Content-Security-Policy, allow our origins:
Content-Security-Policy:frame-src https://widget.flonk.id;script-src https://widget.flonk.id https://api.flonk.id;connect-src https://api.flonk.id https://widget.flonk.id;img-src https://widget.flonk.id data:;
You do not need any CORS or Cross-Origin-Resource-Policy config on your
side — our public assets already send the right cross-origin headers, and the
API handles CORS for the SDK's requests. If the loader script specifically is
blocked, the SDK falls back to its bundled loader and still works (you just lose
dashboard-driven loader branding).
API Issues
401 Unauthorized
Symptom: invalid_api_key error.
Checklist:
- Using correct secret key (not publishable key)
- Key is for correct environment (test/live)
-
Bearerprefix in Authorization header - No extra spaces in key
typescript// Correctheaders: {'Authorization': `Bearer ${secretKey}`,}// Wrongheaders: {'Authorization': secretKey, // Missing Bearer'Authorization': `Bearer ${secretKey} `, // Extra space}
400 Bad Request
Symptom: Request validation errors.
Common causes:
- Invalid or missing data:
typescript{"clientMetadata": {"email": "user@example.com", // recommended"userId": "user_123" // optional}}// All clientMetadata fields are optional, but email is// recommended so webhook events can be matched to users.
- Invalid JSON:
typescript// Use JSON.stringifybody: JSON.stringify(data)// Notbody: data
Webhook Issues
Webhooks Not Received
Checklist:
- Endpoint is publicly accessible (not localhost)
- HTTPS enabled
- URL registered in Flonk Dashboard
- Server returns 200 OK
Test with ngrok for local development:
bashngrok http 3000# Use ngrok URL in Dashboard
Invalid Signature
Symptom: Signature verification fails.
Solutions:
- Use raw body, not parsed JSON:
typescript// NestJS - enable raw bodyapp.useGlobalPipes(new ValidationPipe());app.use(json({ verify: (req, res, buf) => {req.rawBody = buf;}}));
- Verify correct webhook secret
- Pass the right header —
X-Signature(recommended, replay-protected) or theX-Signature-256.constructEventaccepts either. If you verify manually, use a constant-time compare (crypto.timingSafeEqual), not===.
Duplicate Webhooks
Symptom: Same event processed multiple times.
Cause: delivery is at-least-once (Flonk retries on non-200), so duplicates
are expected. Dedupe by event.id in your own store — a unique DB index or
Redis SET NX:
typescriptconst fresh = await redis.set(`whk:${event.id}`, '1', 'PX', 6 * 60_000, 'NX');if (fresh === null) return; // retry — already handledawait processEvent(event);
See Webhooks → Idempotency for the DB-unique-index variant.
Getting Help
If you can't resolve an issue:
- Check Integration Guide
- Check API Reference
- Contact support: support@flonk.id
Include in support requests:
- Session ID
- Error messages
- Timestamp
- Request/response logs (without sensitive data)