TypeScript / JavaScript SDK
fetch). Also works in modern browsers without any polyfills.Installation
npm install @uverify/sdk
# or
yarn add @uverify/sdk
# or
pnpm add @uverify/sdkQuick Start
import { UVerifyClient } from '@uverify/sdk';
const client = new UVerifyClient();
const certificates = await client.verify('a3b4c5d6...');
console.log(certificates);Creating the client
import { UVerifyClient } from '@uverify/sdk';
// Connect to the public API (default)
const client = new UVerifyClient();
// Connect to a self-hosted instance
const client = new UVerifyClient({ baseUrl: 'http://localhost:9090' });
// Register default signing callbacks so you don't pass them on every call
const client = new UVerifyClient({
signMessage: async (message) => wallet.signData(address, message),
signTx: (unsignedTx) => wallet.signTx(unsignedTx, true),
});
// Override the verification base URL for deep links (used by client.apps)
// Defaults automatically: preprod API → preprod app, production API → production app
const client = new UVerifyClient({
signMessage,
signTx,
verifyBaseUrl: 'https://app.uverify.io/verify',
});Verify a certificate
// By data hash
const certs = await client.verify('sha256-or-sha512-hex-hash');
// By transaction hash + data hash
const cert = await client.verifyByTransaction('cardano-tx-hash', 'data-hash');Issue certificates
issueCertificates handles the full flow — build, sign, submit — in one call and returns the Cardano transaction hash on success.
The example below uses mesh.js with a headless wallet. In a browser you would pass api.signTx from a CIP-30 wallet instead.
import { UVerifyClient } from '@uverify/sdk';
import { MeshCardanoHeadlessWallet, AddressType } from '@meshsdk/wallet';
import { KoiosProvider } from '@meshsdk/core';
import { sha256 } from 'js-sha256';
const provider = new KoiosProvider('preprod');
const wallet = await MeshCardanoHeadlessWallet.fromMnemonic({
networkId: 0,
walletAddressType: AddressType.Base,
fetcher: provider,
submitter: provider,
mnemonic: ['word1', 'word2', /* ... */],
});
const address = await wallet.getChangeAddressBech32();
const client = new UVerifyClient({
signMessage: (message) => wallet.signData(address, message),
signTx: (unsignedTx) => wallet.signTx(unsignedTx, true),
});
const txHash = await client.issueCertificates(address, [
{
hash: sha256('Hello, UVerify!'),
algorithm: 'SHA-256',
metadata: { issuer: 'Acme Corp', date: new Date().toISOString() },
},
]);
console.log('Certified at tx:', txHash);Optionally pass a stateId as the last argument to issue under a specific state:
const txHash = await client.issueCertificates(
address,
[{ hash: 'sha256-hash' }],
undefined, // use constructor signTx
'my-state-id',
);User state management
const state = await client.getUserInfo(address);
console.log('Certificates remaining:', state?.countdown);
if (state?.id) {
await client.invalidateState(address, state.id);
await client.optOut(address, state.id);
}A per-call signing callback can be passed as the last argument to any of these methods if you didn’t register one in the constructor.
Low-level access via .core
// Build
const { unsignedTransaction } = await client.core.buildTransaction({
type: 'default',
address: 'addr1...',
stateId: 'your-state-id',
certificates: [{ hash: 'sha256-hash', algorithm: 'SHA-256' }],
});
// Sign with your wallet, then submit
const witnessSet = await wallet.signTx(unsignedTransaction, true);
const txHash = await client.core.submitTransaction(unsignedTransaction, witnessSet);// Two-step user state action (manual)
const challenge = await client.core.requestUserAction({
address: 'addr1...',
action: 'USER_INFO',
});
const { key, signature } = await wallet.signData(address, challenge.message);
const result = await client.core.executeUserAction({
...challenge,
userSignature: signature,
userPublicKey: key,
});
console.log(result.state);Application helpers (.apps)
client.apps provides high-level helpers for the three most common built-in templates. They handle hashing, metadata construction, prefix conventions, and verification URL generation automatically.
issueDiploma
import { UVerifyClient } from '@uverify/sdk';
const client = new UVerifyClient({ signMessage, signTx });
const result = await client.apps.issueDiploma(address, [
{
studentId: 'TUM-2021-0042',
name: 'Maria Müller',
degree: 'Master of Science in Computer Science',
institution: 'Technical University of Munich',
graduationDate: '2024-06-28',
honors: 'Summa Cum Laude', // optional
},
{
studentId: 'TUM-2021-0117',
name: 'Felix Schmidt',
degree: 'Master of Science in Computer Science',
institution: 'Technical University of Munich',
graduationDate: '2024-06-28',
},
]);
console.log('Transaction:', result.txHash);
for (const cert of result.certificates) {
// verifyUrl includes ?name= so the certificate page reveals the recipient's name
console.log(`${cert.name}: ${cert.verifyUrl}`);
}The hash is sha256(studentId). The recipient’s name is hashed and stored as uv_url_name; the plain name is appended as ?name= in the returned verifyUrl. Update policy is set to first.
issueDigitalProductPassport
const { txHash, verifyUrl } = await client.apps.issueDigitalProductPassport(address, {
name: 'EcoCharge Powerbank Pro 200',
manufacturer: 'GreenTech AG',
gtin: '04012345678901',
serialNumber: 'EC200-SN-20240815-00847',
model: 'EC-200-2024',
origin: 'Germany',
manufactured: '2024-08-15',
contact: 'sustainability@greentech-ag.example',
brandColor: '#1a56db',
carbonFootprint: '1.2 kg CO₂e',
recycledContent: '38%',
energyClass: 'A++',
warranty: '3 years',
spareParts: 'Available until 2034',
repairInfo: 'https://greentech-ag.example/repair/ec-200',
recycling: 'Return to any EU-authorised WEEE recycling point.',
materials: { aluminum: '45%', recycled_plastic: '38%', lithium_cells: '12%' },
certifications: { ce: 'CE Marking', rohs: 'RoHS Compliant', energy_star: 'Energy Star 8.0' },
});
// verifyUrl includes ?serial= so the certificate page can reveal the serial number
console.log('Passport URL:', verifyUrl);The hash is sha256(gtin + serialNumber). Material keys are automatically prefixed with mat_; certification keys with cert_. The serial number is hashed as uv_url_serial. Update policy is set to restricted.
issueLaboratoryReport
const result = await client.apps.issueLaboratoryReport(address, [
{
reportId: 'BMD-2024-10-00123',
patientName: 'Sophie Wagner',
labName: 'Berlin Medical Diagnostics GmbH',
contact: 'results@bmd-lab.example',
auditable: true,
values: {
glucose: '5.4 mmol/L',
hba1c: '5.7%',
cholesterol: '4.9 mmol/L',
},
},
]);
for (const cert of result.certificates) {
// verifyUrl includes ?name=&report_id= to reveal patient name and report ID
console.log(`${cert.patientName} / ${cert.reportId}: ${cert.verifyUrl}`);
}The hash is sha256(reportId). Value keys are automatically prefixed with a_. Both patient name and report ID are hashed (uv_url_name, uv_url_report_id) and revealed via URL parameters. Update policy is set to first.
Error handling
import { UVerifyApiError, UVerifyValidationError } from '@uverify/sdk';
try {
const txHash = await client.issueCertificates(address, certs);
} catch (err) {
if (err instanceof UVerifyApiError) {
console.error(`API error ${err.statusCode}:`, err.responseBody);
}
if (err instanceof UVerifyValidationError) {
console.error(err.message);
}
}API Reference
High-level helpers
| Method | Description |
|---|---|
verify(hash) | Look up all on-chain certificates for a data hash |
verifyByTransaction(txHash, dataHash) | Fetch a specific certificate by tx hash + data hash |
issueCertificates(address, certificates, signTx?, stateId?) | Build, sign, and submit; returns tx hash |
getUserInfo(address, signMessage?) | Retrieve the current user state |
invalidateState(address, stateId, signMessage?) | Mark a state as invalid |
optOut(address, stateId, signMessage?) | Remove the user’s state entirely |
Application helpers (.apps)
| Method | Description |
|---|---|
apps.issueDiploma(address, diplomas[], signTx?) | Issue one or more diploma certificates; returns { txHash, certificates[] } |
apps.issueDigitalProductPassport(address, product, signTx?) | Issue a Digital Product Passport; returns { txHash, hash, verifyUrl } |
apps.issueLaboratoryReport(address, reports[], signTx?) | Issue one or more lab report certificates; returns { txHash, certificates[] } |
Low-level core (.core)
| Method | Endpoint |
|---|---|
core.buildTransaction(request) | POST /api/v1/transaction/build |
core.submitTransaction(tx, witnessSet?) | POST /api/v1/transaction/submit |
core.requestUserAction(request) | POST /api/v1/user/request/action |
core.executeUserAction(request) | POST /api/v1/user/state/action |