Implementation patterns for the TrustVerify REST API. There is no TrustVerify SDK package to install — every example below is plain HTTP against endpoints you can call today.
Secure online store transactions with fraud protection
Check both sides of a deal are verified
Financial services with compliance and security
Large-scale business transaction management
// E-commerce checkout — score an order before you accept payment.
// Base URL for both sandbox and live: https://api.trustverify.co.uk/api/v1
// Your key decides which you get — tv_test_sk_… returns fixtures, tv_live_sk_… runs real checks.
const TV_BASE = 'https://api.trustverify.co.uk/api/v1';
async function screenOrder(order) {
const res = await fetch(`${TV_BASE}/fraud/score`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRUSTVERIFY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: order.customer.email,
ip: order.ipAddress,
deviceFP: order.deviceId, // your device fingerprint, if you collect one
userAgent: order.userAgent, // the SHOPPER's UA, not your server's
orderValue: order.total,
orderCount: order.customer.previousOrders,
paymentMethod: order.paymentMethod,
billingAddress: order.billingAddress,
shippingAddress: order.shippingAddress,
}),
});
if (!res.ok) throw new Error(`TrustVerify ${res.status}: ${await res.text()}`);
// { checkId, score, riskTier, action, signals, completedAt }
// score is 0-100 where HIGHER IS SAFER: >=75 low, >=50 medium, >=25 high.
const result = await res.json();
switch (result.action) { // 'approve' | 'review' | 'block'
case 'block': return { status: 'rejected', result };
case 'review': return { status: 'manual_review', result };
default: return { status: 'approved', result };
}
}// Marketplace — check both parties are verified before they transact.
// Base URL for both sandbox and live: https://api.trustverify.co.uk/api/v1
// Your key decides which you get — tv_test_sk_… returns fixtures, tv_live_sk_… runs real checks.
const TV_BASE = 'https://api.trustverify.co.uk/api/v1';
const auth = {
Authorization: `Bearer ${process.env.TRUSTVERIFY_API_KEY}`,
'Content-Type': 'application/json',
};
// Read back a check you ran earlier — you store the checkId from /kyc/verify.
async function getCheck(checkId) {
const res = await fetch(`${TV_BASE}/checks/${checkId}`, { headers: auth });
if (res.status === 404) return null;
if (!res.ok) throw new Error(`TrustVerify ${res.status}: ${await res.text()}`);
return res.json();
}
async function bothPartiesVerified(project) {
const [freelancer, client] = await Promise.all([
getCheck(project.freelancerCheckId),
getCheck(project.clientCheckId),
]);
const passed = (c) => c?.status === 'passed';
if (!passed(freelancer) || !passed(client)) {
return { status: 'kyc_required', freelancer, client };
}
// Escrow cannot be opened through the API. It is opened from the
// TrustVerify dashboard.
return { status: 'verified', freelancer, client };
}// Fintech — run a name check, then pull the evidence report.
// Base URL for both sandbox and live: https://api.trustverify.co.uk/api/v1
// Your key decides which you get — tv_test_sk_… returns fixtures, tv_live_sk_… runs real checks.
const TV_BASE = 'https://api.trustverify.co.uk/api/v1';
const auth = {
Authorization: `Bearer ${process.env.TRUSTVERIFY_API_KEY}`,
'Content-Type': 'application/json',
};
async function screenBeneficiary(payment) {
const res = await fetch(`${TV_BASE}/aml/screen`, {
method: 'POST',
headers: auth,
body: JSON.stringify({
name: payment.beneficiary.name,
type: 'individual', // or 'business' for a company
dateOfBirth: payment.beneficiary.dateOfBirth,
country: payment.beneficiary.country,
}),
});
if (!res.ok) throw new Error(`TrustVerify ${res.status}: ${await res.text()}`);
// { checkId, status: 'clear' | 'match', riskLevel, totalHits, hits[] }
const screening = await res.json();
if (screening.status === 'match') {
// TrustVerify drafts SAR content from these signals for your MLRO to review
// and file — it never files on your behalf.
return { status: 'compliance_review_required', screening };
}
// This screen is point-in-time. Enrol the subject so they are re-screened
// daily and you get an aml.hit webhook the first time a NEW match appears —
// keyed on the sanctions-list entity id, so a changed spelling or confidence
// score will not alert you twice about the same match.
await fetch('https://api.trustverify.co.uk/api/v1/aml/monitor', {
method: 'POST',
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
// Send dateOfBirth/country where you have them: without them a common name
// matches the same unrelated person on every screen, so you get the same
// alert every day.
body: JSON.stringify({ entityId: customerId, name: fullName, dateOfBirth, country }),
});
return { status: 'clear', screening };
}
// Download the tamper-evident PDF for a completed check to keep as evidence.
async function fetchReport(checkId, reportType = 'RPT-002') {
const res = await fetch(`${TV_BASE}/reports/${reportType}/${checkId}.pdf`, {
headers: { Authorization: auth.Authorization },
});
if (!res.ok) throw new Error(`TrustVerify ${res.status}: ${await res.text()}`);
return Buffer.from(await res.arrayBuffer());
}// Enterprise B2B — verify a batch of counterparties before onboarding.
// Base URL for both sandbox and live: https://api.trustverify.co.uk/api/v1
// Your key decides which you get — tv_test_sk_… returns fixtures, tv_live_sk_… runs real checks.
const TV_BASE = 'https://api.trustverify.co.uk/api/v1';
async function verifySupplier(supplier) {
const res = await fetch(`${TV_BASE}/kyb/verify`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRUSTVERIFY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
companyNumber: supplier.registrationNumber,
companyName: supplier.legalName,
jurisdiction: supplier.jurisdiction, // e.g. 'GB'
}),
});
// Your plan's daily API call cap returns 429 with code DAILY_CAP_EXCEEDED
// and a resetsAt timestamp — that one will not clear by retrying.
if (res.status === 429) {
const body = await res.json().catch(() => ({}));
throw new Error(`Rate limited${body.resetsAt ? `; resets at ${body.resetsAt}` : ''}`);
}
if (!res.ok) throw new Error(`TrustVerify ${res.status}: ${await res.text()}`);
// { checkId, status: 'passed' | 'review' | 'failed', riskLevel, company, … }
return res.json();
}
async function verifySuppliers(suppliers, concurrency = 5) {
const results = [];
// Verify in small waves rather than one 100-wide burst: the ceiling that
// binds is the per-IP flood limiter plus your plan's daily cap.
for (let i = 0; i < suppliers.length; i += concurrency) {
const wave = suppliers.slice(i, i + concurrency);
results.push(...await Promise.all(wave.map(verifySupplier)));
}
return results;
}// Webhook handler — verify the signature, then act on the event.
import express from 'express';
import crypto from 'crypto';
const app = express();
app.post('/webhooks/trustverify', express.raw({ type: 'application/json' }), (req, res) => {
// X-TrustVerify-Signature is the raw hex HMAC-SHA256 of the body — there is
// no "sha256=" prefix. Compare in constant time.
const received = String(req.headers['x-trustverify-signature'] ?? '');
const expected = crypto
.createHmac('sha256', process.env.TRUSTVERIFY_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const a = Buffer.from(received, 'utf8');
const b = Buffer.from(expected, 'utf8');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('Invalid signature');
}
// The event name is on the header; the body is the event payload.
const eventType = String(req.headers['x-trustverify-event'] ?? '');
const payload = JSON.parse(req.body);
// These are every event dispatched to a registered endpoint today.
switch (eventType) {
case 'verification.completed': handleVerificationCompleted(payload); break;
case 'fraud.score.generated': handleFraudScore(payload); break;
case 'escrow.released': handleEscrowReleased(payload); break;
case 'escrow.disputed': handleEscrowDisputed(payload); break;
case 'arbitration.resolved': handleArbitrationResolved(payload); break;
// 'check.completed' does NOT arrive here. Per-request callbacks to
// metadata.webhookUrl are paused: an /api/v1 check returns its result in
// the response, and GET /api/v1/checks/{checkId} reads it again.
default:
console.warn('Unhandled TrustVerify event', eventType);
}
// Acknowledge fast. Failed deliveries are retried up to 3 times with
// exponential backoff, then left as 'failed' in the dashboard to replay.
res.status(200).send('OK');
});We do not publish starter repositories or language SDKs. We offer these ready-made e-commerce integrations instead: a WooCommerce plugin and a Shopify app.