Skip to main content

Product Trace API

The Product Trace API provides image-based product verification and authenticity checking with multi-modal feature extraction including barcode detection, OCR, and visual feature matching.

Overview​

The Product Trace API enables you to:

  • Register Products: Store product images with extracted features for later verification
  • Verify Authenticity: Compare query images against registered products
  • Detect Barcodes: Scan and decode barcodes from product images
  • Extract Text: OCR extraction for text matching
  • Multi-Image Fusion: Combine features from multiple angles
  • Async Processing: Handle large-scale product registrations

Endpoints​

Record Product​

Register a product with its features for later verification.

POST /api/product-trace/record​

Register a product with single image.

Request Body:

{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://your-storage.com/product.jpg",
"image_urls": ["https://.../angle1.jpg", "https://.../angle2.jpg"],
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial"
}

Fields:

FieldTypeRequiredDescription
skustring✓Product SKU identifier
identification_codestring✓Batch or serial number for verification
image_urlstring-Single image URL (legacy support)
image_urlsstring[]-Multiple image URLs for multi-angle features
product_namestring-Human-readable product name
descriptionstring-Product description
categorystring-Product category
Backward Compatibility

Both image_url and image_urls are supported. image_urls accepts either a single string or an array of strings for maximum flexibility.

Response:

{
"id": 42,
"user_id": 1,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://your-storage.com/product.jpg",
"image_hash": "a1b2c3d4e5f6...",
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial",
"created_at": "2024-01-15T10:30:00"
}

POST /api/product-trace/record/multi​

Register product with multiple images for feature fusion. This endpoint combines features from all provided images for more robust verification.

Request Body:

{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_urls": [
"https://.../front.jpg",
"https://.../back.jpg",
"https://.../label.jpg"
],
"product_name": "Premium Widget X2000"
}

Feature Fusion Strategy:

  • Global features: Vector averaging for noise suppression
  • Barcodes: Frequency voting + confidence weighting
  • OCR: Union + deduplication across images
  • Fine-grained: Best quality image selection

Response:

{
"success": true,
"message": "Product recorded successfully with multi-image fusion",
"total_images": 3,
"product_info": {
"id": 43,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"has_barcode": true,
"has_ocr": true,
"has_fusion": true
},
"fusion_stats": {
"barcodes_found": 2,
"text_areas": 8,
"features_extracted": 3
}
}

POST /api/product-trace/record/async​

Submit product for asynchronous feature extraction. Use this for large-scale imports or high-throughput scenarios.

Request Body:

{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_urls": [
"https://.../image1.jpg",
"https://.../image2.jpg"
]
}

Response:

{
"record_id": 44,
"status": "pending",
"progress": 0,
"message": "Processing task queued",
"estimated_seconds": 15,
"created_at": "2024-01-15T10:30:00"
}
Processing Time

Async processing typically takes 5-20 seconds depending on image size and server load. Poll the status endpoint for updates.

Processing Status​

GET /api/product-trace/status/record_id}​

Check the status of an asynchronous processing task.

Response:

{
"record_id": 44,
"status": "processing",
"progress": 65,
"message": "Extracting fine-grained features",
"current_phase": "fine_grained_extraction",
"sub_tasks": {
"barcode": "completed",
"ocr": "completed",
"global_features": "completed",
"fine_grained": "in_progress"
},
"has_barcode": true,
"has_ocr": true,
"has_fine_grained": false,
"has_fusion": false,
"started_at": "2024-01-15T10:30:00",
"completed_at": null
}

Status Values:

  • pending: Task queued, not started
  • processing: Feature extraction in progress
  • completed: All features extracted and stored
  • failed: Processing failed, check error_message

Phase Values:

  • barcode_extraction: Barcode detection in progress
  • ocr_extraction: Text extraction in progress
  • global_feature_extraction: Global visual features
  • fine_grained_extraction: Detailed feature extraction
  • feature_fusion: Combining multi-image features
  • database_save: Saving results to database

POST /api/product-trace/status/batch​

Check status of multiple records in a single request.

Request Body:

{
"record_ids": [44, 45, 46, 47]
}

Response:

{
"total_count": 4,
"pending_count": 1,
"processing_count": 1,
"completed_count": 1,
"failed_count": 1,
"results": [
{ /* ProcessingStatus for record 44 */ },
{ /* ProcessingStatus for record 45 */ },
{ /* ProcessingStatus for record 46 */ },
{ /* ProcessingStatus for record 47 */ }
]
}

Query Products​

GET /api/product-trace/products​

Get a paginated list of registered products.

Query Parameters:

ParameterTypeRequiredDescription
skustringNoFilter by SKU (supports partial match)
categorystringNoFilter by category (supports partial match)
pageintegerNoPage number (default: 1)
page_sizeintegerNoItems per page (default: 20, max: 100)

Response:

{
"total": 150,
"page": 1,
"page_size": 20,
"products": [
{
"id": 42,
"user_id": 1,
"sku": "PROD-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://storage.com/img.jpg",
"image_hash": "abc123...",
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial",
"created_at": "2024-01-15T10:30:00",
"already_exists": false,
"message": "Product recorded successfully",
"has_barcode": true,
"has_ocr": true,
"has_fusion": true,
"has_fine_grained": true,
"has_enhanced": true
}
]
}

GET /api/product-trace/products/product_id}​

Get details for a single product by its database ID.

Response: Same as single item in product list.

GET /api/product-trace/products/lookup​

Multi-condition product lookup. Find products by ID, SKU, identification code, OCR text hash, or barcode MD5. Supports combined queries with AND semantics.

Query Parameters:

ParameterTypeRequiredDescription
product_idintegerNo*Product database ID
skustringNo*Exact SKU match
identification_codestringNo*Exact identification code match
text_hashstringNo*OCR text normalized MD5 hash
barcode_md5stringNo*Barcode MD5 value (fuzzy match in barcode_data)

*At least one query parameter is required. All parameters use AND logic when combined.

Example: Lookup by SKU

GET /api/product-trace/products/lookup?sku=PROD-12345

Example: Lookup by text_hash

GET /api/product-trace/products/lookup?text_hash=d41d8cd98f00b204e9800998ecf8427e

Example: Lookup by barcode_md5

GET /api/product-trace/products/lookup?barcode_md5=a1b2c3d4e5f6...

Response:

{
"total": 1,
"products": [
{
"id": 42,
"user_id": 1,
"sku": "PROD-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial",
"image_url": "https://storage.com/img.jpg",
"image_hash": "abc123...",
"original_image_urls": "[\"https://.../angle1.jpg\"]",
"has_barcode": true,
"has_ocr": true,
"has_fusion": true,
"has_fine_grained": true,
"has_enhanced": true,
"text_hash": "d41d8cd98f00b204e9800998ecf8427e",
"text_fingerprint": "e99a18c428cb38d5...",
"barcode_data": "{\"barcode_md5s\":[\"a1b2c3d4e5f6...\"]}",
"ocr_data": "{\"text\":\"...\",\"success\":true}",
"ocr_text": "Extracted OCR text content here",
"fine_grained_data": "{...}",
"processing_status": "completed",
"processing_progress": 100,
"processing_message": null,
"verification_count": 5,
"last_verified_at": "2024-01-20T15:00:00",
"created_at": "2024-01-15T10:30:00",
"updated_at": "2024-01-15T10:30:00"
}
]
}

Error Response (404):

{
"detail": "未找到匹配的产品"
}

DELETE /api/product-trace/products/product_id}​

Delete a product record and its associated verification history (cascade delete).

Response:

{
"message": "删除成功",
"product_id": 42
}

Verification History​

GET /api/product-trace/verification-history​

Get paginated verification history records.

Query Parameters:

ParameterTypeRequiredDescription
product_idintegerNoFilter by product ID
pageintegerNoPage number (default: 1)
page_sizeintegerNoItems per page (default: 20, max: 100)

Response:

{
"total": 50,
"page": 1,
"page_size": 20,
"history": [
{
"id": 1,
"product_feature_id": 42,
"similarity_score": 0.95,
"threshold": 0.85,
"is_verified": true,
"created_at": "2024-01-20T15:00:00"
}
]
}

Verify Product​

POST /api/product-trace/verify​

Verify product authenticity by comparing against registered features.

Request Body:

{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://.../query_image.jpg",
"image_urls": ["https://.../query1.jpg", "https://.../query2.jpg"],
"threshold": 0.85
}

Verification Pipeline (Hash-Only Mode - Default):

Query Image
↓
[Stage 1] Barcode MD5 Match (<10ms, O(log n))
↓ (if match found)
Return: Verified (similarity=1.0)
↓ (if no match)
[Stage 2] Text Hash Match (<10ms, O(log n))
↓ (if match found)
Return: Verified (similarity=1.0)
↓ (if no match)
Return: Not Verified (NO_HASH_HIT)

Verification Pipeline (Legacy Mode - Optional):

Set SCAN_HASH_ONLY_MODE=false to enable full cascade:

Query Image
↓
[Stage 1] Barcode Match (<10ms)
↓ (if match found)
Return: Verified
↓ (if no match)
[Stage 2] Text Hash Match (<10ms)
↓ (if match found)
Return: Verified
↓ (if no match)
[Stage 3] Global Feature Match (<100ms)
↓ (if score > threshold)
Return: Verified
↓ (if score < reject)
Return: Not Verified
↓ (inconclusive)
[Stage 4] Fine-Grained + OCR Match (2-6s)
↓
Final Verification Result

Hash-Only Mode Response (Match Found):

{
"matched": true,
"match_source": "barcode",
"product_info": {
"id": 42,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000"
},
"similarity": 1.0,
"match_stage": "hash_only_barcode",
"diagnostics": {
"extracted_text_hash": "a1b2c3d4e5f6...",
"extracted_barcodes": ["5901234123457"],
"ocr_text_preview": "PRODUCT-12345 BATCH-2024-001...",
"conflict": false
}
}

Hash-Only Mode Response (No Match):

{
"matched": false,
"match_source": null,
"reason": "NO_HASH_HIT",
"similarity": 0.0,
"match_stage": "hash_only_no_match",
"diagnostics": {
"extracted_text_hash": "a1b2c3d4e5f6...",
"extracted_barcodes": [],
"ocr_text_preview": "PRODUCT-12345...",
"conflict": false
}
}

Legacy Mode Response:

{
"is_verified": true,
"similarity_score": 0.92,
"threshold": 0.85,
"product_info": {
"id": 42,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000"
},
"message": "Product verified via fine-grained feature matching"
}

Verification Methods:

MethodSpeedUse CaseMode
barcode_match<10msFast verification when barcode is visibleHash-Only
text_hash_match<10msOCR text fingerprint matchingHash-Only
global_feature_match<100msQuick visual matchingLegacy
fine_grained_match2-6sDetailed, high-accuracy verificationLegacy
ocr_text_match1-3sText-based verificationLegacy
Hash-Only Mode

By default, the verification pipeline uses Hash-Only Mode which provides deterministic, millisecond-level matching based on barcode and OCR text hashes. This mode:

  • Faster: <10ms response time
  • Deterministic: No probability-based similarity scores
  • Reliable: Consistent results across platforms

To enable legacy visual similarity matching, set environment variable SCAN_HASH_ONLY_MODE=false.

Tuning Verification
  • Lower threshold for more lenient matching (0.7-0.8) - Legacy mode only
  • Higher threshold for stricter matching (0.85-0.95) - Legacy mode only
  • Default threshold is 0.8, which provides a good balance

POST /api/product-trace/verify/multi​

Verify using multiple query images from different angles.

Request Body:

{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_urls": [
"https://.../query_front.jpg",
"https://.../query_back.jpg"
],
"threshold": 0.8
}

Response:

{
"is_verified": true,
"max_similarity": 0.94,
"avg_similarity": 0.89,
"threshold": 0.8,
"total_images": 2,
"verified_images": 2,
"product_info": { /* product info */ },
"message": "Product verified from multiple angles",
"image_results": [
{
"image_index": 0,
"similarity": 0.94,
"method": "fine_grained_match",
"is_verified": true
},
{
"image_index": 1,
"similarity": 0.83,
"method": "global_feature_match",
"is_verified": true
}
]
}

Image Search (Scan)​

POST /api/product-trace/scan​

Scan an image and find matching products without requiring specific SKU/identification code. In Hash-Only Mode (default), this endpoint performs barcode and text hash based matching.

Request Body:

{
"image_url": "https://.../unknown_product.jpg",
"image_urls": ["https://.../unknown1.jpg", "https://.../unknown2.jpg"],
"top_k": 5,
"request_id": "req-20260101-001",
"notification_url": "https://your-server.com/callback/scan",
"notification_token": "your-auth-token"
}
ParameterTypeRequiredDescription
image_urlstringNo*Single image URL
image_urlsstring[]No*Multiple image URLs for multi-view scanning
top_kintegerNoMax results (default: 5)
request_idstringNoUnique request identifier for dedup & callback passthrough. Same user + same request_id limited to 1 request per 30s.
notification_urlstringNoCallback URL for async scan result notification
notification_tokenstringNoToken set in X-Verification-Token header of callback request

*At least one of image_url or image_urls must be provided.

Hash-Only Mode Response (Match Found):

{
"total_count": 1,
"top_k": 5,
"mode": "hash_only",
"results": [
{
"product_info": {
"id": 42,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000"
},
"similarity": 1.0,
"match_source": "barcode",
"match_stage": "hash_only_barcode"
}
]
}

Hash-Only Mode Response (No Match):

{
"total_count": 0,
"top_k": 5,
"mode": "hash_only",
"results": [],
"diagnostics": {
"extracted_text_hash": "a1b2c3d4e5f6...",
"extracted_barcodes": [],
"ocr_text_preview": "PRODUCT-12345..."
}
}

Legacy Mode Response (SCAN_HASH_ONLY_MODE=false):

{
"total_count": 12,
"threshold": 0.7,
"top_k": 5,
"mode": "legacy",
"results": [
{
"product_info": { /* product data */ },
"similarity_score": 0.94,
"global_similarity": 0.94,
"fine_similarity": 0.91,
"match_type": "strong_match"
},
{
"product_info": { /* product data */ },
"similarity_score": 0.82,
"global_similarity": 0.82,
"fine_similarity": null,
"match_type": "potential_match"
}
]
}
Scan vs Verify
  • Verify (/verify): Requires sku + identification_code to match against a specific product
  • Scan (/scan): No SKU required, searches all products for hash matches
  • Both endpoints use Hash-Only Mode by default

Barcode Detection​

POST /api/product-trace/barcode/detect​

Detect and decode barcodes from images without storing product information.

Request Body:

{
"image_url": "https://.../product_with_barcode.jpg",
"image_urls": ["https://.../barcode1.jpg", "https://.../barcode2.jpg"],
"return_image_data": true
}

Response:

{
"success": true,
"total_images": 2,
"total_barcodes": 3,
"results": [
{
"image_index": 0,
"image_url": "https://.../barcode1.jpg",
"image_size": { "width": 1920, "height": 1080 },
"barcodes_count": 2,
"barcodes": [
{
"type": "EAN13",
"data": "5901234123457",
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"confidence": 0.98,
"rect": {
"left": 450,
"top": 300,
"width": 200,
"height": 80
}
},
{
"type": "QRCODE",
"data": "https://example.com/product/12345",
"md5": "a1b2c3d4e5f6...",
"confidence": 0.95
}
]
}
],
"unique_barcodes": [
{ /* unique barcode 1 */ },
{ /* unique barcode 2 */ }
],
"message": "Successfully detected 3 barcodes from 2 images"
}

Supported Barcode Types:

  • EAN-8, EAN-13
  • UPC-A, UPC-E
  • Code 39, Code 93, Code 128
  • ITF, Codabar
  • QR Code, Data Matrix
  • PDF417, Aztec
OCR Barcode/Serial Extraction

When zxing-cpp fails to detect physical barcodes in an image, the system also extracts numeric and alphanumeric patterns from OCR text and stores them in ocr_data.ocr_extracted_barcodes:

  • EAN13: 13-digit numeric strings (e.g., 5901234123457)
  • UPC-A: 12-digit numeric strings
  • EAN8: 8-digit numeric strings
  • ITF14: 14-digit numeric strings
  • SERIAL: 8-32 character alphanumeric strings (e.g., SN:ABC123456)

These extracted codes are available for hash-based matching during Scan/Verify operations through the same ocr_data JSON column. Each extracted code is stored with its MD5 hash and type label.

Fine-Grained Verification​

POST /api/product-trace/verify/fine​

Detailed verification using fine-grained features and OCR text matching.

Request Body:

{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://.../query_image.jpg",
"threshold": 0.7
}

Response:

{
"success": true,
"verified": true,
"message": "Product verified with fine-grained features",
"base_similarity": 0.82,
"fine_grained_similarity": 0.89,
"threshold": 0.7,
"text_hash_match": true,
"text_areas_count": 6,
"sku_patterns_found": ["PROD", "12345"],
"product_info": { /* product data */ }
}

POST /api/product-trace/ann/search​

High-performance Approximate Nearest Neighbor vector search. In Hash-Only Mode, this endpoint returns hash-based matches only.

Request Body:

{
"image_url": "https://.../query.jpg",
"top_k": 10,
"min_similarity": 0.5,
"vector_field": "fusion_vector"
}

Hash-Only Mode Response:

{
"total": 1,
"top_k": 10,
"mode": "hash_only",
"results": [
{
"product_info": { /* product data */ },
"similarity": 1.0,
"match_source": "barcode"
}
]
}

Legacy Mode Response:

{
"total": 150,
"top_k": 10,
"min_similarity": 0.5,
"mode": "legacy",
"vector_field": "fusion_vector",
"results": [
{
"product_info": { /* product data */ },
"similarity": 0.94
}
]
}

POST /api/product-trace/ann/verify​

Fast two-stage ANN verification: coarse search + fine verify. In Hash-Only Mode, uses barcode/text_hash matching instead of vector similarity.

Request Body:

{
"image_url": "https://.../query.jpg",
"threshold": 0.7,
"coarse_top_k": 50
}

Hash-Only Mode Response:

{
"success": true,
"verified": true,
"mode": "hash_only",
"match_source": "text_hash",
"message": "Product verified via hash match",
"product_info": { /* product data */ }
}

Legacy Mode Response:

{
"success": true,
"verified": true,
"mode": "legacy",
"message": "Product verified via ANN search",
"candidates_count": 50,
"best_similarity": 0.88,
"threshold": 0.7,
"best_match": { /* product data */ }
}

Async Notification Callback​

Scan/Verify Notification​

When scan_notification_url or verification_notification_url is configured for a user, the system will asynchronously POST results to the specified URL after Scan or Verify operations complete.

Scan Notification Payload:

{
"verification_id": 0,
"is_verified": true,
"similarity_score": 1.0,
"threshold": 0.8,
"message": "扫描完成,匹配到 1 个商品",
"timestamp": "2026-06-02T17:00:00",
"request_id": "req-20260101-001",
"total_matches": 1
}

Verify Notification Payload:

{
"verification_id": 42,
"is_verified": true,
"similarity_score": 1.0,
"threshold": 0.8,
"message": "Product verified via hash match",
"timestamp": "2026-06-02T17:00:00",
"request_id": "req-20260101-001"
}
FieldTypeDescription
verification_idintegerVerification record ID (0 for scan)
is_verifiedbooleanWhether the product was verified
similarity_scorefloatMatch similarity score (1.0 for hash matches)
thresholdfloatMatch threshold used
messagestringHuman-readable result message
timestampstringISO 8601 callback timestamp
request_idstringThe request_id from the original request, returned unchanged for correlation
total_matchesintegerNumber of matched results (scan only)

Callback Headers:

HeaderValue
Content-Typeapplication/json
X-Verification-TokenThe notification_token from the original request

Note: The result dict passed to the notification service is fully transparent — any additional fields in the result (e.g., product_info, match_source) are also included in the callback payload.

Configuration​

Hash-Only Mode​

The Product Trace API supports two verification modes:

ModeEnvironment VariableBehavior
Hash-Only (Default)SCAN_HASH_ONLY_MODE=trueBarcode → Text Hash → NO_HASH_HIT
LegacySCAN_HASH_ONLY_MODE=falseFull 5-stage cascade with visual similarity

Key Differences:

AspectHash-Only ModeLegacy Mode
Response Time<10ms100ms - 8s
Matching BasisDeterministic hashProbability-based similarity
DINOv2 UsageNoneFull feature extraction
OCR UsageText hash onlyFull text matching
Consistency100% deterministicPlatform-dependent

Use Hash-Only Mode when:

  • Barcode or OCR text is available on products
  • Millisecond-level response time is required
  • Deterministic results are preferred over probabilistic

Use Legacy Mode when:

  • Products have no barcode or OCR text
  • Visual similarity matching is specifically needed
  • Fine-grained feature comparison is required
Endpoint Coverage

Hash-Only Mode applies to these endpoints:

  • /verify
  • /verify/upload
  • /verify/multi
  • /scan
  • /scan/upload
  • /ann/verify
  • /ann/verify/upload
  • /ann/search

Endpoints NOT affected by Hash-Only Mode:

  • /record* (feature extraction)
  • /fine-grained/* (fine-grained features)
  • /barcode/* (barcode detection)
  • /migration/* (feature migration)

Migration (Feature Upgrade)​

POST /api/product-trace/migrate/start​

Start feature migration to extract new format features for existing records.

Request Body:

{
"target_version": "v2",
"batch