Skip to main content

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:

AttributeRecommendation
Minimum Size25mm × 25mm
Resolution300 DPI minimum
Error CorrectionLevel M (15%)
ColorsBlack on white (high contrast)
Quiet Zone4 modules minimum

Application Options

Product TypeRecommended Method
Luxury goodsCompanion card with QR
Wine bottlesCapsule seal or neck tag
Collectible cardsSlab/case label
Art/paintingsFrame-back tag
General productsVoid label or sticker

Step 4: Scan with a Phone

When a customer scans the QR code:

  1. Camera app opens the URL automatically
  2. Browser navigates to id.optropic.com
  3. 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?