Skip to 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:

json
{
    "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
skustringProduct SKU identifier
identification_codestringBatch 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:

json
{
    "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:

json
{
    "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:

json
{
    "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:

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

Response:

json
{
    "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/

Check the status of an asynchronous processing task.

Response:

json
{
    "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:

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

Response:

json
{
    "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:

json
{
    "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/

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

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

Example: Lookup by text_hash

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

Example: Lookup by barcode_md5

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

Response:

json
{
    "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):

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

DELETE /api/product-trace/products/

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

Response:

json
{
    "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:

json
{
    "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:

json
{
    "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):

json
{
    "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):

json
{
    "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:

json
{
    "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:

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

Response:

json
{
    "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:

json
{
    "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):

json
{
    "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):

json
{
    "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):

json
{
    "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:

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

Response:

json
{
    "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:

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

Response:

json
{
    "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:

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

Hash-Only Mode Response:

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

Legacy Mode Response:

json
{
    "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:

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

Hash-Only Mode Response:

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

Legacy Mode Response:

json
{
    "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:

json
{
    "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:

json
{
    "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:

json
{
    "target_version": "v2",
    "batch