Identity Verification (KYC)

Purpose: this documentation describes how to integrate the Roommatik identity verification system. You can use the web widget in one of its six operation modes, consume the REST API directly, or combine both approaches.

The widget covers everything from document capture to full biometric verification (document + selfie + face comparison). The REST API exposes the same services for server-to-server integrations.

Full technical reference · Download Postman collection

1. Quick start

Copy the snippet below, replace YOUR_API_KEY with your key, and open it in a browser. This example runs the full verification flow: document capture, selfie capture, and face comparison.

<!DOCTYPE html>
<html>
<head>
    <script src="https://sdk.roommatik.com/kyc/v1/roommatik-kyc.js"></script>
    <link rel="stylesheet" href="https://sdk.roommatik.com/kyc/v1/roommatik-kyc.css">
</head>
<body>
    <roommatik-kyc
        mode="verify"
        api-url="https://kyc.roommatik.com"
        api-key="YOUR_API_KEY"
        lang="en">
    </roommatik-kyc>
    <script>
        document.querySelector('roommatik-kyc')
            .addEventListener('kyc-complete', (e) => {
                if (e.detail.success) {
                    alert('Identity verified: ' + e.detail.document.DocumentNumber);
                }
            });
    </script>
</body>
</html>

2. Prerequisites

  • API key: authentication key issued by Roommatik.
  • Base URL: https://kyc.roommatik.com.
  • HTTPS: the widget requires an HTTPS origin (or localhost) to access the camera.

Contact your Roommatik account manager to obtain your credentials.

3. Widget modes

Choose the mode that matches your use case via the mode attribute:

ModeDescriptionAPI key required
capture-onlyCaptures images (document or face) and hands them back to your code. No API calls are made.No
documentDocument capture with OCR and field extraction.Yes
verifyFull verification: document + selfie + face comparison.Yes
face-compareCapture or upload two face images and return a similarity score.Yes
face-embedCapture a face and extract a 512-dimensional face embedding vector.Yes

4. Widget attributes

AttributeTypeDefaultDescription
modestringcapture-onlyOperation mode (see previous table).
api-urlstringAPI base URL. Required for all modes except capture-only.
api-keystringAPI key. Required for all modes except capture-only.
langstringenLanguage: en, es, fr, de, it, pt.
primary-colorstring (hex)#3b82f6Primary UI color.
two-sidedbooleantrueTwo-sided document (ID card, residence permit). Set two-sided="false" for passports and single-sided documents. Only applies to document and verify modes.
capture-targetstringdocumentIn capture-only mode: document or face.
input-modestringcameraInput method: camera, upload or both. The upload option accepts JPG, PNG and multi-page PDF files.
scan-modestringocrFor document/verify modes: ocr (camera capture with OCR) or digitalIdQr (read the QR code from the Spanish DNIe / miDNI app).
combine-imagesbooleanfalseCombines front and back into a single image and submits them in one API call.
return-typestringDataComma-separated values: Data, Face, Signature. For example "Data,Face".
is-mapping-appliedbooleantrueReturns standardized field names (FullName, DocumentNumber, etc.).
max-image-widthnumber1600Maximum captured image width in pixels.
jpeg-qualitynumber (0–1)0.92JPEG compression quality.
auto-startbooleantrueAutomatically start the camera when the widget mounts.
labelsJSON stringOverrides built-in translations for specific labels.

5. Events

EventModese.detail
kyc-capturedcapture-only{ frontImage, backImage, combinedImage, frontBlob, backBlob, combinedBlob, qrData? }
kyc-completeall other modesObject discriminated by mode (see section 6).
kyc-errorallError object.
kyc-cancelall

6. Result by mode

The e.detail object of the kyc-complete event is discriminated by mode:

In widget modes that return document data (document and verify), the document fields are delivered inside a data object (or document in verify mode), with PascalCase names when is-mapping-applied="true".

6.1. document mode

{
    "mode": "document",
    "success": true,
    "data": {
        "Name": "JOHN",
        "Surname": "DOE SMITH",
        "FullName": "JOHN DOE SMITH",
        "DocumentNumber": "AB123456",
        "DocumentType": "Identity Card",
        "DateOfBirth": "15/03/1990",
        "DateOfExpiry": "20/01/2030",
        "Sex": "M",
        "Nationality": "ESP"
    }
}

6.2. verify mode

{
    "mode": "verify",
    "success": true,
    "document": {
        "FullName": "JOHN DOE SMITH",
        "DocumentNumber": "AB123456",
        "DateOfBirth": "15/03/1990",
        "DateOfExpiry": "20/01/2030"
    },
    "verification": {
        "verified": true,
        "similarity": 87.3,
        "distance": 0.127
    }
}

6.3. face-compare mode

{
    "mode": "face-compare",
    "success": true,
    "verified": true,
    "similarity": 87.3,
    "distance": 0.127
}

6.4. face-embed mode

{
    "mode": "face-embed",
    "success": true,
    "results": [{
        "embedding": [0.0123, -0.0456, "...512 values"],
        "boundingBox": { "x": 50, "y": 30, "width": 120, "height": 150 },
        "confidence": 0.99
    }]
}

7. capture-only mode — uploading images from the browser

If you need the captured images to bypass your own server, the widget can upload them directly to a presigned S3 URL and hand back only the object key:

const kyc = document.querySelector('roommatik-kyc');
kyc.uploadCapturedImage = {
    getPresignedUrl: async () => {
        const res = await fetch('/my-backend/presign', { method: 'POST' });
        return await res.json(); // { uploadUrl, key }
    },
    onUploaded: (key) => {
        console.log('Image uploaded:', key);
    }
};

When combined with two-sided="true", combine-images="true" is enforced so there is a single upload per capture.

8. React integration

import { KycWidget } from '@roommatik/id-document-capture';
import '@roommatik/id-document-capture/style.css';

<KycWidget
    mode="verify"
    apiUrl="https://kyc.roommatik.com"
    apiKey="YOUR_API_KEY"
    lang="en"
    onComplete={(result) => {
        if (result.mode === 'verify' && result.success) {
            console.log('Verified:', result.verification.verified);
            console.log('Document:', result.document);
        }
    }}
    onError={(err) => console.error(err)}
/>

9. SDK (without UI)

If you already have your own capture interface, use the KycClient SDK to talk to the API:

import { KycClient } from '@roommatik/id-document-capture';

const client = new KycClient({
    apiUrl: 'https://kyc.roommatik.com',
    apiKey: 'YOUR_API_KEY',
    returnType: ['Data', 'Face'],
    isMappingApplied: true
});

// Document OCR (single or dual side)
const result = await client.verify(frontImage, backImage);

// Face comparison
const compare = await client.compareFaces(imageA, imageB);

// 512-dimensional face embedding
const embed = await client.embedFace(image);

// miDNI QR decoding
const qr = await client.verifyQr(qrBase64);

In addition, KycClient exposes static utilities to encode and decode face vectors in a compact format suitable for QR codes:

const qrPayload = KycClient.encodeEmbeddingForQr(embedding); // ~700 characters
const vector = KycClient.decodeEmbeddingFromQr(qrPayload);

10. REST API (v2)

All gateway endpoints authenticate via the X-Api-Key header. The Content-Type is application/json unless stated otherwise. You can optionally include the X-Account-Name header with your account name to speed up authentication.

MethodEndpointDescription
POST/api/v2/kycDocument OCR — single side (image) or dual side (image1 + image2).
POST/api/v2/mrzMRZ-only extraction (passports and ID cards).
POST/api/v2/face/compareFace comparison.
POST/api/v2/face/embed512-dimensional face embedding extraction.
POST/api/v2/qr/midniSpanish DNIe (miDNI) QR decoding.
GET/healthPublic liveness check.

10.1. POST /api/v2/kyc

Request body (single side):

{
    "image": "BASE64_IMAGE_WITHOUT_PREFIX",
    "returnType": ["Data", "Face"],
    "IsMappingApplied": true
}

Request body (dual side):

{
    "image1": "BASE64_FRONT",
    "image2": "BASE64_BACK",
    "returnType": ["Data", "Face"],
    "IsMappingApplied": true
}
FieldTypeDescription
image / image1 / image2stringBase64-encoded image without the data:image/...;base64, prefix.
imageUrlstringAlternative to image: an S3 presigned URL the service will fetch.
returnTypestring[]What to return: Data, Face, Signature.
IsMappingAppliedbooleanReturns fields with standardized names.
face_vectorbooleanCompute a 512-dimensional face embedding. Requires Face in returnType.
faceImagestringBase64 selfie to compare against the document face. The response will include FaceMatchAccuracy.

Response (200 OK):

{
    "Data": {
        "Name": "JOHN",
        "Surname": "DOE SMITH",
        "FullName": "JOHN DOE SMITH",
        "DocumentNumber": "AB123456",
        "DocumentType": "Identity Card",
        "DateOfBirth": "15/03/1990",
        "DateOfExpiry": "20/01/2030",
        "Sex": "M",
        "Nationality": "ESP"
    },
    "GeoData": {
        "Region": "Community of Madrid",
        "Province": "Madrid",
        "PostalCode": "28001"
    },
    "ResponseTime": 1250,
    "Description": "Spain - Identity Card",
    "Face": "BASE64_CROPPED_FACE"
}

The Face, Signature, FaceVector, FaceMatchAccuracy and GeoData fields are omitted when they do not apply. If the image does not yield extractable data, the API responds with 204 No Content.

10.2. POST /api/v2/face/compare

Supports four input modes:

// 1) Two photos
{ "image1": "BASE64_A", "image2": "BASE64_B" }

// 2) Document + selfie (the face is auto-cropped from the document)
{ "document": "BASE64_DOC", "selfie": "BASE64_SELFIE" }

// 3) Precomputed vector + photo
{ "vector": [0.0123, ...], "image": "BASE64" }

// 4) Two vectors (fastest)
{ "vector1": [0.0123, ...], "vector2": [0.0456, ...] }

Response:

{
    "verified": true,
    "similarity": 87.3,
    "distance": 0.127,
    "face1": { "detected": true, "confidence": 0.99, "boundingBox": {...} },
    "face2": { "detected": true, "confidence": 0.98, "boundingBox": {...} },
    "processingTimeMs": 320
}

10.3. POST /api/v2/face/embed

// Single image
{ "image": "BASE64" }

// Batch
{ "images": ["BASE64_1", "BASE64_2"] }

Response:

{
    "results": [{
        "faceDetected": true,
        "confidence": 0.99,
        "boundingBox": {...},
        "embedding": [0.0123, -0.0456, "...512 values"]
    }],
    "processingTimeMs": 280
}

10.4. POST /api/v2/qr/midni

Decodes the Spanish DNIe (miDNI) QR. The endpoint expects the raw binary payload extracted from the QR, not an image of it:

{ "Base64Data": "PAYLOAD_BASE64" }
// or
{ "HexData": "PAYLOAD_HEX" }

Response:

{
    "Data": {
        "DocumentNumber": "12345678Z",
        "FirstName": "JOHN",
        "Surname": "DOE SMITH",
        "DateOfBirth": "15-03-1990",
        "Gender": "M",
        "ExpiryDate": "20-01-2030",
        "Nationality": "ES",
        "SignatureValid": "true"
    },
    "GeoData": { "Region": "...", "Province": "...", "PostalCode": "..." },
    "ResponseTime": 45,
    "Description": "miDNI QR (Complete) - Verification: Valid"
}

10.5. Full cURL example

curl -X POST https://kyc.roommatik.com/api/v2/kyc \
    -H "X-Api-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
        "image": "BASE64_IMAGE",
        "returnType": ["Data", "Face"],
        "IsMappingApplied": true
    }'

11. HTTP response codes

CodeMeaning
200Operation successful.
204No data could be extracted — low image quality or unrecognized document.
400Bad request: malformed JSON, missing required fields, or invalid parameters.
401Unauthorized — API key missing or invalid.
402Insufficient credits for this operation.
403Account blocked, license expired, or IP not allowed.
422Face endpoints: no face was detected in the submitted image.
429Rate limit exceeded (per minute or per day).
500Internal service error.
503Service temporarily unavailable.

12. Limits and restrictions

ItemDetails
Rate limitingConfigurable per account (per minute and per day).
Timeout30 seconds by default.
Image formatJPEG or PNG (Base64, presigned URL, or multipart upload). Multi-page PDF from the widget.
HTTPSThe widget requires HTTPS to access the camera.
Supported browsersChrome, Firefox, Safari, and Edge (recent versions).
Max image sizeRecommended under 2 MB per image.