Web SDK
Drop-in verification widget for browsers via CDN and npm. (Coming soon.)
⚠️ Próximamente — not yet released. This document describes the intended shape of the embeddable VerifyMX Web SDK / widget. Neither the npm package nor the CDN URL referenced below are live yet. For now, integrate via the REST API directly (see README.md) — your backend creates a verification, then redirects the user to the hosted capture flow at
verify.verifymx.xyz(the buyer portal). We'll announce when the embeddable widget ships.
The VerifyMX Web SDK will provide a drop-in verification widget that handles camera capture, document photography, liveness detection, and OTP confirmation entirely in the browser. Your backend stays clean — it only receives the final webhook event.
Installation (when released)
CDN (script tag)
Add this script to your page, ideally in <head> with defer:
<script defer src="https://cdn.verifymx.xyz/sdk/v1/verifymx.min.js"></script>
npm
npm install @verifymx/web-sdk
# or
pnpm add @verifymx/web-sdk
Quick Start — Button Embed
The simplest integration: a button that opens the verification modal.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<script defer src="https://cdn.verifymx.xyz/sdk/v1/verifymx.min.js"></script>
</head>
<body>
<button id="verify-btn">Verificar identidad</button>
<script>
document.getElementById('verify-btn').addEventListener('click', async () => {
// 1. Your backend creates a verification and returns the ID
const res = await fetch('/api/create-verification', { method: 'POST' });
const { verificationId } = await res.json();
// 2. Open the VerifyMX widget
const vmx = new VerifyMX({
verificationId,
onComplete: (result) => {
console.log('Verification complete:', result.status);
// Redirect or update UI
},
onError: (error) => {
console.error('SDK error:', error.code, error.message);
},
onExit: () => {
console.log('User closed the widget without completing');
},
});
vmx.open();
});
</script>
</body>
</html>
Your backend route (/api/create-verification) example in Node.js:
app.post('/api/create-verification', async (req, res) => {
const token = await getVerifyMXToken(); // cache this, expires in 1h
const response = await fetch('https://api.verifymx.xyz/v1/verifications', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
workflowId: process.env.VERIFYMX_WORKFLOW_ID,
externalId: req.user.id, // link to your user
}),
});
const { verification } = await response.json();
res.json({ verificationId: verification.id });
});
Configuration Options
const vmx = new VerifyMX({
// REQUIRED
verificationId: 'ver_01HXYZ...',
// CALLBACKS
onComplete: (result) => {}, // called when all steps finish
onError: (error) => {}, // called on unrecoverable error
onExit: () => {}, // called when user closes modal
// OPTIONAL
language: 'es', // 'es' (default) or 'en'
theme: 'light', // 'light' (default) or 'dark'
primaryColor: '#0057FF', // hex, overrides button/accent color
logoUrl: 'https://myapp.mx/logo.png', // shown in widget header
// FLOW CONTROL
allowRetry: true, // show retry button on failure (default true)
showProgress: true, // show step progress bar (default true)
// CAMERA
facingMode: 'environment', // 'environment' (back camera, default) or 'user'
cameraResolution: { width: 1280, height: 720 },
// CONTAINER (for embedded mode — omit for modal)
container: '#verify-container',
});
Events
onComplete(result)
Called when all workflow steps finish. result shape:
{
verificationId: 'ver_01HXYZ...',
status: 'APPROVED' | 'REJECTED' | 'MANUAL_REVIEW',
steps: [
{ type: 'DOCUMENT_OCR', status: 'PASSED' },
{ type: 'FACE_MATCH', status: 'PASSED' },
]
}
Do not trust the client-side status for access control decisions — always confirm
via server-side webhook or API call (GET /v1/verifications/:id).
onError(error)
{
code: 'CAMERA_PERMISSION_DENIED' | 'NETWORK_ERROR' | 'SESSION_EXPIRED' | 'UNKNOWN',
message: 'Human-readable description'
}
Common error codes:
| Code | Cause |
|---|---|
CAMERA_PERMISSION_DENIED | User denied camera access |
NETWORK_ERROR | API unreachable |
SESSION_EXPIRED | verificationId is no longer valid |
MAX_RETRIES_EXCEEDED | Too many failed capture attempts |
onExit()
Called when the user closes the modal or clicks "Cancel" before completing.
Camera Capture API
The SDK exposes a headless capture API if you want to build a fully custom UI:
import { VerifyMXCapture } from '@verifymx/web-sdk';
const capture = new VerifyMXCapture({
verificationId: 'ver_01HXYZ...',
videoElement: document.getElementById('camera-preview'),
});
await capture.start(); // requests camera permission and starts preview
// Capture document front
const frontDoc = await capture.captureDocument('front');
// { blob: Blob, mimeType: 'image/jpeg' }
// Capture selfie
const selfie = await capture.captureSelfie();
// Upload to API (or let the SDK handle it)
await capture.upload([
{ fieldname: 'front', blob: frontDoc.blob, mimeType: frontDoc.mimeType },
{ fieldname: 'selfie', blob: selfie.blob, mimeType: selfie.mimeType },
]);
capture.stop(); // releases camera
Instance Methods
const vmx = new VerifyMX({ verificationId: '...' });
vmx.open(); // open the modal (or mount to container)
vmx.close(); // programmatically close/unmount
vmx.destroy(); // remove DOM nodes and release camera
Customization (CSS)
The widget exposes CSS custom properties for theming. Set them on :root or the
widget container before calling vmx.open():
:root {
--vmx-primary: #0057FF; /* primary button and accent color */
--vmx-primary-hover: #0041CC;
--vmx-bg: #FFFFFF; /* modal background */
--vmx-surface: #F5F5F5; /* step card background */
--vmx-text: #111111;
--vmx-text-secondary: #666666;
--vmx-border-radius: 12px; /* button and card corner radius */
--vmx-font-family: 'Inter', sans-serif;
}
Framework Integration
React
import { useEffect, useRef } from 'react';
import { VerifyMX } from '@verifymx/web-sdk';
interface Props {
verificationId: string;
onComplete: (result: VerifyMXResult) => void;
}
export function VerifyButton({ verificationId, onComplete }: Props) {
const vmxRef = useRef<VerifyMX | null>(null);
useEffect(() => {
vmxRef.current = new VerifyMX({
verificationId,
onComplete,
onError: (err) => console.error(err),
onExit: () => console.log('User exited'),
});
return () => {
vmxRef.current?.destroy();
};
}, [verificationId]);
return (
<button onClick={() => vmxRef.current?.open()}>
Verificar identidad
</button>
);
}
Vue 3
<template>
<button @click="openWidget">Verificar identidad</button>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { VerifyMX } from '@verifymx/web-sdk';
const props = defineProps<{ verificationId: string }>();
const emit = defineEmits(['complete']);
let vmx: VerifyMX | null = null;
onMounted(() => {
vmx = new VerifyMX({
verificationId: props.verificationId,
onComplete: (result) => emit('complete', result),
onError: (err) => console.error(err),
});
});
onUnmounted(() => vmx?.destroy());
const openWidget = () => vmx?.open();
</script>
Angular
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { VerifyMX } from '@verifymx/web-sdk';
@Component({
selector: 'app-verify-button',
template: '<button (click)="open()">Verificar identidad</button>',
})
export class VerifyButtonComponent implements OnInit, OnDestroy {
@Input() verificationId!: string;
private vmx!: VerifyMX;
ngOnInit() {
this.vmx = new VerifyMX({
verificationId: this.verificationId,
onComplete: (r) => console.log(r.status),
onError: (e) => console.error(e),
});
}
open() { this.vmx.open(); }
ngOnDestroy() { this.vmx.destroy(); }
}
Browser Compatibility
| Browser | Minimum version |
|---|---|
| Chrome | 90+ |
| Firefox | 88+ |
| Safari | 14.1+ |
| Edge | 90+ |
| Chrome Android | 90+ |
| Safari iOS | 14.5+ |
The widget requires getUserMedia (camera access). HTTPS is mandatory in production —
most browsers block getUserMedia on insecure origins.
Security Notes
- Never embed your raw API key or bearer token in client-side code. Create the
verification server-side and pass only the
verificationIdto the browser. - The
verificationIdgrants access to upload documents for that single session only. It cannot list other verifications or access org settings. - Final approval decisions should always be read from a server-side webhook or
GET /v1/verifications/:idcall — never trust the client-sideonCompleteresult alone for access control.