Your First Verification
This guide walks through a complete end-to-end flow: generating a code, printing it on a product, scanning it, and verifying authenticity.
Overview
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Generate │───▶│ Print │───▶│ Scan │───▶│ Verify │
│ Code │ │ QR Code │ │ with Phone │ │ Authentic │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
Step 1: Generate an Authenticated Code
Use the API to generate a code for your product:
curl -X POST https://api.optropic.com/api/v1/code/generate \
-H "x-api-key: optr_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"gtin": "04260799580008",
"serialNumber": "UNIT-001",
"batchNumber": "2026-Q1"
}'
Response:
{
"url": "https://id.optropic.com/01/04260799580008/21/UNIT-001/10/2026-Q1?sig=Kx7mN2...",
"gtin": "04260799580008",
"serial": "UNIT-001",
"signature": "Kx7mN2..."
}
Save the url — this is what you'll encode into a QR code.
Step 2: Generate a QR Code
Encode the URL into a QR code. Use any QR code library:
Node.js (using qrcode)
import QRCode from 'qrcode';
import fs from 'fs';
const url = 'https://id.optropic.com/01/04260799580008/21/UNIT-001/10/2026-Q1?sig=Kx7mN2...';
// Generate PNG
await QRCode.toFile('product-qr.png', url, {
width: 300,
margin: 2,
errorCorrectionLevel: 'M'
});
// Or generate SVG for print
const svg = await QRCode.toString(url, { type: 'svg' });
fs.writeFileSync('product-qr.svg', svg);
Python (using qrcode)
import qrcode
url = 'https://id.optropic.com/01/04260799580008/21/UNIT-001/10/2026-Q1?sig=Kx7mN2...'
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_M,
box_size=10,
border=4,
)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save('product-qr.png')
Step 3: Print and Apply
Print the QR code and apply it to your product:
Print Specifications
| Attribute | Recommendation |
|---|---|
| Minimum Size | 25mm × 25mm |
| Resolution | 300 DPI minimum |
| Error Correction | Level M (15%) |
| Colors | Black on white (high contrast) |
| Quiet Zone | 4 modules minimum |
Application Options
| Product Type | Recommended Method |
|---|---|
| Luxury goods | Companion card with QR |
| Wine bottles | Capsule seal or neck tag |
| Collectible cards | Slab/case label |
| Art/paintings | Frame-back tag |
| General products | Void label or sticker |
Step 4: Scan with a Phone
When a customer scans the QR code:
- Camera app opens the URL automatically
- Browser navigates to
id.optropic.com - Resolver displays verification result
The customer sees a verification page showing:
- ✅ Authentic — Product is verified genuine
- Product details (name, serial, batch)
- Scan count and verification timestamp
Step 5: Programmatic Verification
For backend integrations, verify programmatically:
async function verifyProduct(qrContent) {
const response = await fetch('https://api.optropic.com/api/v1/code/verify', {
method: 'POST',
headers: {
'x-api-key': process.env.OPTROPIC_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: qrContent }),
});
const result = await response.json();
switch (result.verdict) {
case 'AUTHENTIC':
console.log('✅ Product verified as authentic');
console.log(`Serial: ${result.serial}`);
console.log(`Scan #${result.metadata.totalScans}`);
return { authentic: true, data: result };
case 'COUNTERFEIT':
console.error('❌ COUNTERFEIT DETECTED');
// Alert security team
await alertSecurityTeam(result);
return { authentic: false, reason: 'counterfeit' };
case 'SUSPICIOUS':
console.warn('⚠️ Suspicious activity detected');
console.log('Risk indicators:', result.riskIndicators);
return { authentic: null, reason: 'review_needed' };
}
}
Error Handling
Handle common scenarios:
async function safeVerify(url) {
try {
const response = await fetch('https://api.optropic.com/api/v1/code/verify', {
method: 'POST',
headers: {
'x-api-key': process.env.OPTROPIC_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url }),
});
if (response.status === 401) {
throw new Error('Invalid API key');
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
throw new Error(`Rate limited. Retry in ${retryAfter}s`);
}
if (response.status === 400) {
const error = await response.json();
throw new Error(`Invalid request: ${error.error.message}`);
}
return await response.json();
} catch (error) {
console.error('Verification failed:', error.message);
// Fall back to manual verification or show error to user
return { verdict: 'ERROR', message: error.message };
}
}
Complete Integration Example
Here's a full React component for scanning and verifying:
import { useState } from 'react';
import { QrReader } from 'react-qr-reader';
function ProductVerifier() {
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
const handleScan = async (data) => {
if (!data || loading) return;
setLoading(true);
try {
const response = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: data }),
});
setResult(await response.json());
} finally {
setLoading(false);
}
};
return (
<div>
<QrReader onResult={handleScan} constraints={{ facingMode: 'environment' }} />
{loading && <p>Verifying...</p>}
{result && (
<div className={`result ${result.verdict.toLowerCase()}`}>
{result.verdict === 'AUTHENTIC' && (
<>
<h2>✅ Authentic Product</h2>
<p>Serial: {result.serial}</p>
<p>Verified at: {new Date().toLocaleString()}</p>
</>
)}
{result.verdict === 'COUNTERFEIT' && (
<>
<h2>❌ Warning: Counterfeit</h2>
<p>This product could not be verified.</p>
</>
)}
</div>
)}
</div>
);
}
What's Next?
- Auction Integration — Build chain-of-custody tracking
- Offline Verification — Verify without network
- API Reference — Full endpoint documentation