Quickstart
An integration has three parts:
- Your server signs in to Prove ID and creates a transaction token for the user.
- Your page starts the SDK with that token. The SDK talks to Prove ID on its own.
- Your server reads the verification result from Prove ID with the
transactionIdthe page sends back.
1. Get a token on your server
Section titled “1. Get a token on your server”The SDK uses a transaction token to connect to Prove ID on the user’s behalf. Your server creates it in two calls:
| Call | Purpose | Lifetime |
|---|---|---|
POST /oauth/access_token |
Signs your server in with your API credentials (form-encoded, client_credentials grant). |
30 minutes by default (expires_in). Cache it and reuse it for every user. |
POST /link/create-token |
Creates one transaction token for one verification. | Until expiresAt in the response, and only until that flow finishes: a finished flow can’t be started again with the same token. |
The optional idTypes field limits the documents allowed in this transaction (IDCARD, OLDIDCARD, DRIVERCARD, PASSPORT, BLUECARD, FOREIGNID). customerId attaches the transaction to a customer you already created in Prove ID.
// Node.js 18+ (built-in fetch)const API = process.env.PROVEID_API ?? 'https://demo.api.proveid.io';
let cached; // { token, expiresAt }
async function accessToken() { if (cached && cached.expiresAt > Date.now() + 60_000) return cached.token; const res = await fetch(`${API}/oauth/access_token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.PROVEID_CLIENT_ID, client_secret: process.env.PROVEID_CLIENT_SECRET, }), }); if (!res.ok) throw new Error(`Prove ID sign-in failed: ${res.status}`); const data = await res.json(); cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 }; return cached.token;}
export async function createTransactionToken() { const res = await fetch(`${API}/link/create-token`, { method: 'POST', headers: { Authorization: `Bearer ${await accessToken()}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ idTypes: ['IDCARD'] }), }); if (!res.ok) throw new Error(`Creating the transaction token failed: ${res.status}`); return res.json(); // { transactionToken, expiresAt }}import osimport time
import requests
API = os.environ.get("PROVEID_API", "https://demo.api.proveid.io")_cached = {"token": None, "expires_at": 0.0}
def access_token(): if _cached["token"] and _cached["expires_at"] > time.time() + 60: return _cached["token"] res = requests.post( f"{API}/oauth/access_token", data={ # form-encoded "grant_type": "client_credentials", "client_id": os.environ["PROVEID_CLIENT_ID"], "client_secret": os.environ["PROVEID_CLIENT_SECRET"], }, timeout=30, ) res.raise_for_status() data = res.json() _cached.update(token=data["access_token"], expires_at=time.time() + data["expires_in"]) return _cached["token"]
def create_transaction_token(): res = requests.post( f"{API}/link/create-token", headers={"Authorization": f"Bearer {access_token()}"}, json={"idTypes": ["IDCARD"]}, timeout=30, ) res.raise_for_status() return res.json() # {"transactionToken": ..., "expiresAt": ...}using System.Net.Http.Headers;using System.Net.Http.Json;using System.Text.Json;
// http.BaseAddress = new Uri("https://demo.api.proveid.io/") (keep the trailing slash)public sealed class ProveIdClient(HttpClient http, string clientId, string clientSecret){ private string? _token; private DateTimeOffset _expiresAt;
private async Task<string> AccessTokenAsync() { if (_token is not null && _expiresAt > DateTimeOffset.UtcNow.AddMinutes(1)) return _token; var res = await http.PostAsync("oauth/access_token", new FormUrlEncodedContent(new Dictionary<string, string> { ["grant_type"] = "client_credentials", ["client_id"] = clientId, ["client_secret"] = clientSecret, })); res.EnsureSuccessStatusCode(); var data = await res.Content.ReadFromJsonAsync<JsonElement>(); _token = data.GetProperty("access_token").GetString()!; _expiresAt = DateTimeOffset.UtcNow.AddSeconds(data.GetProperty("expires_in").GetInt32()); return _token; }
private async Task<HttpRequestMessage> AuthorizedAsync(HttpMethod method, string path) { var req = new HttpRequestMessage(method, path); req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await AccessTokenAsync()); return req; }
public async Task<string> CreateTransactionTokenAsync() { using var req = await AuthorizedAsync(HttpMethod.Post, "link/create-token"); req.Content = JsonContent.Create(new { idTypes = new[] { "IDCARD" } }); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode(); var data = await res.Content.ReadFromJsonAsync<JsonElement>(); return data.GetProperty("transactionToken").GetString()!; }}API=https://demo.api.proveid.io
# 1. Sign in (form-encoded)ACCESS_TOKEN=$(curl -s -X POST "$API/oauth/access_token" \ -d grant_type=client_credentials \ -d client_id="$PROVEID_CLIENT_ID" \ -d client_secret="$PROVEID_CLIENT_SECRET" | jq -r .access_token)
# 2. Create a transaction tokencurl -s -X POST "$API/link/create-token" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"idTypes": ["IDCARD"]}'# → {"transactionToken": "…", "expiresAt": "…"}Expose this through an endpoint of your own (for example POST /api/kyc/token) that only signed-in users can call, and return just transactionToken to the page. Create a new token for every verification.
2. Add the component to your page
Section titled “2. Add the component to your page”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" /> <title>Identity verification</title> <style> html, body, techsign-kyc { margin: 0; height: 100%; } </style></head><body> <techsign-kyc token="TRANSACTION_TOKEN" domain="https://demo.api.proveid.io" lang="en" enable-face="true" ></techsign-kyc>
<script type="module"> import '/sdk/techsign-kyc.js';
const kyc = document.querySelector('techsign-kyc');
kyc.addEventListener('complete', (e) => { // send e.detail.transactionId to your server console.log('complete', e.detail); });
kyc.addEventListener('error', (e) => { // e.detail.code, e.detail.message, e.detail.retryable console.error('error', e.detail); }); </script></body></html>The component starts the flow as soon as it is added to the page. Set domain to the environment you use.
Render TRANSACTION_TOKEN into the page from your server template, or fetch it first and add the component afterwards:
const res = await fetch('/api/kyc/token', { method: 'POST' });const { transactionToken } = await res.json();
const kyc = document.createElement('techsign-kyc');kyc.setAttribute('token', transactionToken);kyc.setAttribute('domain', 'https://demo.api.proveid.io');kyc.setAttribute('lang', 'en');// add the complete / error listeners as above, then:document.body.append(kyc);3. Verify the result on your server
Section titled “3. Verify the result on your server”The complete event tells you the flow has finished. Don’t base your decision on the result in the browser: send transactionId to your server and read the result from Prove ID with GET /id/id-info-with-tid/{transactionId}.
The checks run during the flow are listed in controlResults, each with its entryType, entryScore and entryInterpretation (true when the check passed). The examples below reuse accessToken() from step 1 and accept the verification only when every check passed; adapt the rule to your own risk policy.
export async function getVerification(transactionId) { const res = await fetch(`${API}/id/id-info-with-tid/${encodeURIComponent(transactionId)}`, { headers: { Authorization: `Bearer ${await accessToken()}` }, }); if (!res.ok) throw new Error(`Reading the result failed: ${res.status}`); const info = await res.json(); const controls = info.controlResults ?? []; const passed = controls.length > 0 && controls.every((c) => c.entryInterpretation === true); return { passed, info };}from urllib.parse import quote
def get_verification(transaction_id): res = requests.get( f"{API}/id/id-info-with-tid/{quote(transaction_id, safe='')}", headers={"Authorization": f"Bearer {access_token()}"}, timeout=30, ) res.raise_for_status() info = res.json() controls = info.get("controlResults") or [] passed = bool(controls) and all(c.get("entryInterpretation") is True for c in controls) return passed, info// Add to ProveIdClient from step 1public async Task<(bool Passed, JsonElement Info)> GetVerificationAsync(string transactionId){ using var req = await AuthorizedAsync(HttpMethod.Get, $"id/id-info-with-tid/{Uri.EscapeDataString(transactionId)}"); var res = await http.SendAsync(req); res.EnsureSuccessStatusCode(); var info = await res.Content.ReadFromJsonAsync<JsonElement>(); var controls = info.TryGetProperty("controlResults", out var list) ? list.EnumerateArray().ToList() : []; var passed = controls.Count > 0 && controls.All(c => c.TryGetProperty("entryInterpretation", out var ok) && ok.ValueKind == JsonValueKind.True); return (passed, info);}curl -s "$API/id/id-info-with-tid/$TRANSACTION_ID" \ -H "Authorization: Bearer $ACCESS_TOKEN" | jq '.controlResults'The full response model and the other result services are in the API Reference tab.
Single file (IIFE)
Section titled “Single file (IIFE)”For pages that can’t use type="module":
<script src="/sdk/techsign-kyc.iife.js"></script>
<techsign-kyc token="TRANSACTION_TOKEN" lang="en" enable-face="false"></techsign-kyc>
<script> const kyc = document.querySelector('techsign-kyc'); kyc.addEventListener('complete', (e) => console.log(e.detail)); kyc.addEventListener('error', (e) => console.error(e.detail));</script>With a bundler
Section titled “With a bundler”import '@techsign/kyc';import { warmupAssets } from '@techsign/kyc';
// Optional: download the models before the user reaches the flowvoid warmupAssets({ enableFace: true });Then use the <techsign-kyc> tag in your template.