Best Practices
Recommendations for integrating VeroID
Security
Server-Side Only
Never call the VeroID API from client-side code:
// ✅ DO: Server-side API route
export async function POST(req) {
const data = await req.json();
const result = await fetch('https://api.veroid.com.au/v1/verify', {
headers: { 'X-API-Key': process.env.VEROID_API_KEY },
body: JSON.stringify(data),
});
return result.json();
}Store Keys in Environment Variables
// ✅ DO: Use environment variables const apiKey = process.env.VEROID_API_KEY; // ❌ DON'T: Hardcode keys const apiKey = 'sk_live_abc123...';
Error Handling
Implement Retries
Only S (system error) responses are transient and safe to retry. Y, N, and D are definitive outcomes - do not retry them. HTTP 429 document cooldowns and DVS_DOCUMENT_LOCKED responses also require waiting - honour Retry-After rather than retrying in a tight loop (see below).
async function verifyWithRetry(data, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const result = await verify(data);
// Y, N, D are definitive - return immediately
if (result.status !== 'error') return result;
// S = system error at issuer or DVS Hub - safe to retry with backoff
if (result.responseCode === 'S') {
await sleep(1000 * Math.pow(2, attempt));
continue;
}
return result;
}
}Document lockout and repeat submissions
DVS and issuing agencies limit how often the same document can be verified in a short period. If a user fixes a typo and clicks verify again immediately, or your integration auto-retries unchanged document data, the issuer may temporarily lock that document.
This applies to live verification only. Sandbox does not enforce document cooldowns.
- Do not resubmit the same document details in quick succession - wait for the user to correct their input, then submit once
- Do not auto-retry
NorDoutcomes with the same payload - Honour the
Retry-Afterheader on HTTP429responses - Debounce verify buttons and disable repeat submits while a request is in flight
VeroID applies two live protections before and after a DVS lock:
| Code | HTTP | When |
|---|---|---|
DOCUMENT_COOLDOWN | 429 | The same document was submitted again within about 1 minute |
DVS_DOCUMENT_LOCKED | 500 | DVS or the issuer returned "Document Temporarily Locked" - wait about 5 minutes before retrying the same document |
const response = await fetch('https://api.veroid.com.au/v1/verify', { ... });
const result = await response.json();
if (response.status === 429 && result.code === 'DOCUMENT_COOLDOWN') {
const waitSeconds = Number(response.headers.get('Retry-After') ?? result.retryAfterSeconds);
// Show the user a "please wait" message - do not retry immediately
return;
}
if (response.status === 500 && result.code === 'DVS_DOCUMENT_LOCKED') {
const waitSeconds = Number(response.headers.get('Retry-After') ?? result.retryAfterSeconds);
// DVS locked this document - wait before submitting the same details again
return;
}Handle All Outcomes
const result = await verify(data);
switch (result.responseCode) {
case 'Y':
// Data matches the issuer record
break;
case 'N':
// Data does not match - check result.errors for field-level detail
if (result.errors?.length) {
console.log(result.errors[0].field, result.errors[0].message);
}
break;
case 'D':
// Data error at the issuer (record not held at source)
break;
case 'S':
// System error - may retry
break;
}Expanded Responses
When DVS returns field-level detail (VersionNumber=2), it appears in the errors array on the POST response. Capture it at verification time - it is not stored and cannot be retrieved later via GET. See the API reference.
- Surface
errors[].messageto help users correct mistyped fields - Use
errors[].fieldto highlight the relevant form input when present - Do not treat
NorDas HTTP errors - they are valid verification outcomes - Do not retry
NorD; onlySis transient
Compliance
Data Handling
- Don't store PII longer than necessary
- Don't log sensitive document numbers
- Do store verification IDs for audit trails
// ✅ DO: Log verification ID only
logger.info('Verification completed', {
verificationId: result.verificationId,
status: result.status
});
// ❌ DON'T: Log PII
logger.info('Verification', {
name: data.givenName,
licenceNumber: data.licenceNumber
});