CertiLock QR Decoder API
Overview
The CertiLock QR Decoder API is a specialized ASP.NET Core Web API application that decodes QR codes containing both overt (visible) and covert (hidden, encrypted) messages. It provides a secure, authenticated REST API for uploading QR code images and extracting both layers of information.
Production URL
Base URL: https://certilock-api.scottsdalemint.com
Key Features
- Dual-Layer QR Decoding: Extracts both visible and hidden encrypted messages from QR codes
- Multi-Format Image Support: Accepts JPEG, PNG, BMP, GIF, WebP, and TIFF image formats
- API Key Authentication: Secure endpoint access with configurable API keys
- IP-Restricted Test Client: Built-in web interface for testing (restricted to whitelisted IPs)
- x86 Architecture: Optimized for 32-bit Windows environments (required by native decoder library)
- CORS Enabled: Supports cross-origin requests for flexible client integration
Architecture
Technology Stack
- .NET 10.0: Latest ASP.NET Core Web API framework
- Platform: x86 (32-bit) - required by PeltaDecoder.dll
- Image Processing: SixLabors.ImageSharp 3.1.6 for grayscale conversion
- Native Decoder: PeltaDecoder.dll (Windows 32-bit native library)
- Hosting: Windows Server 2019 Standard with Plesk 18.0.77 / IIS
Core Components
PeltaApi/
├── Controllers/
│ └── QRDecoderController.cs # API endpoints (/api/qr/decode, /api/qr/health)
├── Services/
│ ├── PeltaNative.cs # P/Invoke declarations for PeltaDecoder.dll
│ ├── PeltaQRDecoder.cs # Main decoder service (singleton)
│ └── ImageConverter.cs # Image-to-grayscale conversion
├── Middleware/
│ ├── ApiKeyAuthenticationMiddleware.cs # X-API-Key validation
│ └── IpRestrictionMiddleware.cs # IP whitelist for test client
├── native/
│ └── PeltaDecoder.dll # Native QR decoder library
└── wwwroot/
└── index.html # Test client interface
Decode Workflow
graph TD
A[Image Upload] --> B[Grayscale Conversion]
B --> C[qr_decOpen]
C --> D[qr_decAnalyze]
D --> E{QR Found?}
E -->|Yes| F[qr_decGetMessage]
E -->|No| G[Error Response]
F --> H[Strip 3-byte Prefix]
H --> I[qr_decGetIndpSize]
I --> J{Covert Present?}
J -->|Yes| K[qr_decGetIndependentKeyed]
J -->|No| L[Return Overt Only]
K --> M[Decrypt Covert]
M --> N[qr_decClose]
L --> N
N --> O[JSON Response]
How It Works
- Image Upload: Client uploads QR code image via HTTP POST
- Grayscale Conversion: ImageSharp converts image to 8-bit grayscale byte array
- Decoder Initialization: PeltaDecoder.dll allocates decoder resources with grayscale data
- QR Analysis: Native decoder scans image bounds for QR code patterns
- Overt Message Extraction: Decodes visible QR data (strips 3-byte "]Q1" prefix)
- Covert Message Extraction: If present, decrypts hidden message using derived master key
- Metadata Extraction: Retrieves QR version and error correction level
- Response: Returns JSON with both messages, version, and ECC level
- Resource Cleanup: Decoder resources freed via qr_decClose()
API Reference
Authentication
All API endpoints (except /api/qr/health) require authentication via the X-API-Key header.
Header Format:
X-API-Key: your-api-key-here
Response on Missing/Invalid Key:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"success": false,
"error": "Invalid or missing API key"
}
Endpoints
1. Decode QR Code
Endpoint: POST /api/qr/decode
Authentication: Required (X-API-Key header)
Content-Type: multipart/form-data
Request Body:
file(required): QR code image file- Supported formats: JPEG, PNG, BMP, GIF, WebP, TIFF
- Max size: No enforced limit (governed by server/IIS settings)
Success Response (200 OK):
{
"success": true,
"overtMessage": "Visible QR code message",
"covertMessage": "Hidden encrypted message (if present)",
"qrVersion": 5,
"eccLevel": 2
}
Field Descriptions:
success: Alwaystrueon successful decodeovertMessage: The visible QR code data (3-byte prefix removed)covertMessage: Hidden encrypted payload (empty string if none)qrVersion: QR code version (1-40, higher = more data capacity)eccLevel: Error correction level (0=L, 1=M, 2=Q, 3=H)
Error Response (400 Bad Request):
{
"success": false,
"error": "Unsupported image format. Supported: JPEG, PNG, BMP, GIF, WebP, TIFF"
}
{
"success": false,
"error": "No QR code detected in the image"
}
Error Response (500 Internal Server Error):
{
"success": false,
"error": "Failed to process image: [specific error message]"
}
2. Health Check
Endpoint: GET /api/qr/health
Authentication: Not required (public endpoint)
Success Response (200 OK):
{
"status": "healthy",
"service": "Pelta QR Decoder API",
"version": "1.0.0",
"platform": "x86"
}
Use Cases:
- Load balancer health checks
- Monitoring system availability
- Deployment verification
- Platform architecture confirmation
Usage Examples
cURL
Decode QR Code:
curl -X POST "https://certilock-api.scottsdalemint.com/api/qr/decode" \
-H "X-API-Key: your-api-key-here" \
-F "file=@/path/to/qrcode.png"
Health Check:
curl "https://certilock-api.scottsdalemint.com/api/qr/health"
PowerShell
Decode QR Code:
$apiKey = "your-api-key-here"
$imagePath = "C:\path\to\qrcode.png"
$uri = "https://certilock-api.scottsdalemint.com/api/qr/decode"
$headers = @{
"X-API-Key" = $apiKey
}
$form = @{
file = Get-Item -Path $imagePath
}
$response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Form $form
$response | ConvertTo-Json
Health Check:
Invoke-RestMethod -Uri "https://certilock-api.scottsdalemint.com/api/qr/health"
C# / .NET
Decode QR Code:
using System.Net.Http;
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your-api-key-here");
var form = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(File.ReadAllBytes(@"C:\path\to\qrcode.png"));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png");
form.Add(fileContent, "file", "qrcode.png");
var response = await client.PostAsync(
"https://certilock-api.scottsdalemint.com/api/qr/decode",
form
);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
JavaScript / Fetch API
Decode QR Code:
const apiKey = "your-api-key-here";
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
fetch('https://certilock-api.scottsdalemint.com/api/qr/decode', {
method: 'POST',
headers: {
'X-API-Key': apiKey
},
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Overt Message:', data.overtMessage);
console.log('Covert Message:', data.covertMessage);
console.log('QR Version:', data.qrVersion);
console.log('ECC Level:', data.eccLevel);
})
.catch(error => console.error('Error:', error));
Python
Decode QR Code:
import requests
api_key = "your-api-key-here"
url = "https://certilock-api.scottsdalemint.com/api/qr/decode"
headers = {
"X-API-Key": api_key
}
with open("/path/to/qrcode.png", "rb") as file:
files = {"file": file}
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
data = response.json()
print(f"Overt Message: {data['overtMessage']}")
print(f"Covert Message: {data['covertMessage']}")
print(f"QR Version: {data['qrVersion']}")
print(f"ECC Level: {data['eccLevel']}")
else:
print(f"Error: {response.status_code}")
print(response.json())
Configuration
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"PeltaApi": "Information"
}
},
"AllowedHosts": "*",
"ApiKeys": [
"your-production-api-key-here",
"your-secondary-api-key-here"
],
"IpRestriction": {
"AllowedIps": [
"12.235.66.14",
"50.221.25.7"
]
}
}
Configuration Sections
ApiKeys:
- Array of valid API keys for authentication
- Add/remove keys as needed for client management
- Keys are case-sensitive
- No length restrictions (recommend 32+ characters)
IpRestriction.AllowedIps:
- Whitelist for test client access (
/and/index.html) - Does NOT affect API endpoints (accessible from any IP with valid API key)
- IPv4 addresses only
- Empty array = no restrictions (not recommended for production)
Test Client
Access
URL: https://certilock-api.scottsdalemint.com/
IP Restriction: Only accessible from whitelisted IPs (configured in IpRestriction.AllowedIps)
Features
- Drag-and-drop or click-to-browse file upload
- Real-time configuration of API endpoint and key
- Visual display of decoded results
- Raw JSON response viewer
- Support for all image formats (JPEG, PNG, BMP, GIF, WebP, TIFF)
Usage
- Navigate to the root URL from a whitelisted IP
- Enter API endpoint:
https://certilock-api.scottsdalemint.com/api/qr/decode - Enter your API key
- Upload or drag-drop a QR code image
- Click "Upload & Decode"
- View results: overt message, covert message, QR metadata
Deployment
Server Requirements
- Operating System: Windows Server 2016+ (deployed on Windows Server 2019)
- .NET Runtime: .NET 10.0 Runtime (x86 version required)
- Web Server: IIS with ASP.NET Core Module V2
- Hosting Platform: Plesk 18.0.77
IIS Configuration
Application Pool Settings:
- .NET CLR Version: No Managed Code
- Enable 32-Bit Applications: True ⚠️ (critical for PeltaDecoder.dll)
- Pipeline Mode: Integrated
- Identity: ApplicationPoolIdentity
Build & Publish
From the project directory:
dotnet publish -c Release -p:Platform=x86 -o publish
Important: Always use -p:Platform=x86 flag to ensure 32-bit compilation.
Deployment Steps
-
Build and publish the application (see command above)
-
Upload
publish/folder contents to Pleskhttpdocsdirectory -
Verify all files are present:
PeltaApi.dllPeltaApi.exeappsettings.jsonnative/PeltaDecoder.dllwwwroot/index.html- All dependency DLLs
-
Configure IIS Application Pool in Plesk:
- Navigate to Hosting Settings
- Check "Enable 32-bit applications"
- Set .NET version to "No Managed Code"
-
Update
appsettings.jsonwith production API keys -
Restart application pool
-
Verify health endpoint:
curl https://certilock-api.scottsdalemint.com/api/qr/health
File Structure in Production
httpdocs/
├── PeltaApi.exe
├── PeltaApi.dll
├── appsettings.json
├── web.config (auto-generated)
├── native/
│ └── PeltaDecoder.dll
├── wwwroot/
│ └── index.html
└── [dependency DLLs]
Security
API Key Management
- Store API keys in
appsettings.json(not in code) - Use strong, randomly generated keys (32+ characters recommended)
- Rotate keys periodically
- Different keys per client/environment for tracking and revocation
IP Restrictions
- Test client (
/and/index.html) protected by IP whitelist - API endpoints (
/api/qr/*) accessible from any IP with valid key - Configure whitelist in
IpRestriction.AllowedIpssection - Changes require application restart
HTTPS
- Always use HTTPS in production
- API keys transmitted in HTTP headers (vulnerable if unencrypted)
- SSL certificate managed via Plesk
- Let's Encrypt automatic renewal configured
Input Validation
- File type validation (MIME type and extension)
- Image format validation via ImageSharp
- Maximum file size governed by IIS settings (default: 30MB)
- Malicious file rejection via image processing layer
Troubleshooting
Common Issues
404 Not Found on Root (/)
Cause: Middleware order incorrect or wwwroot missing
Solution:
- Verify
UseDefaultFiles()comes beforeUseStaticFiles()in Program.cs - Verify
wwwroot/index.htmlexists in publish folder - Check IIS physical path points to correct directory
401 Unauthorized
Cause: Missing or invalid API key
Solution:
- Verify
X-API-Keyheader is present in request - Check key matches value in
appsettings.json(case-sensitive) - Ensure API key is not empty or whitespace
- Check for extra spaces or special characters
403 Forbidden on Test Client
Cause: IP address not whitelisted
Solution:
- Add your IP to
IpRestriction.AllowedIpsinappsettings.json - Restart application via Plesk
- Verify your actual public IP (use
https://whatismyip.com) - Check for IPv4 vs IPv6 issues
500 Internal Server Error - "Failed to load PeltaDecoder.dll"
Cause: 32-bit DLL cannot load in 64-bit process
Solution:
- Enable 32-bit applications in IIS Application Pool (Plesk Hosting Settings)
- Verify platform target is x86 in .csproj
- Rebuild with
-p:Platform=x86flag - Check
native/PeltaDecoder.dllexists in httpdocs
No QR Code Detected
Cause: Image quality, size, or contrast issues
Solution:
- Ensure QR code is clearly visible and in focus
- Try images with higher resolution (minimum 300x300px recommended)
- Verify image is not rotated or distorted
- Check QR code has sufficient contrast (dark on light background)
- Ensure QR code occupies significant portion of image
Performance
Benchmarks
- Average decode time: 50-150ms (depends on image size and QR complexity)
- Throughput: 10-20 requests/second (single instance)
- Memory usage: ~50-100MB base + ~5-10MB per concurrent request
- Startup time: ~2-3 seconds (decoder initialization)
Optimization Tips
- Use compressed images (JPEG with 85% quality recommended)
- Resize large images before upload (max 2000x2000px sufficient)
- Implement client-side caching for repeated decodes
- Consider load balancing for high-traffic scenarios
- Monitor memory usage under sustained load
Scaling
- Vertical: Increase server CPU/RAM for more concurrent requests
- Horizontal: Deploy multiple instances behind load balancer (session-less API)
- CDN: Use CDN for test client static assets (wwwroot/*)
- Caching: Implement Redis for response caching if decode patterns repeat
Monitoring & Logging
Health Monitoring
Implement automated health checks via monitoring service or cron job:
# Example health check script
#!/bin/bash
response=$(curl -s -o /dev/null -w "%{http_code}" https://certilock-api.scottsdalemint.com/api/qr/health)
if [ $response -ne 200 ]; then
echo "API health check failed: HTTP $response"
# Send alert
fi
Log Levels
- Information: API requests, decoder initialization, platform detection
- Warning: Invalid images, decode failures, IP restriction blocks
- Error: Decoder errors, image processing failures
- Critical: Startup failures, decoder initialization errors
Log Location
- Console: Visible in Plesk application logs
- EventLog: Windows Event Viewer (Application)
- Plesk Logs:
/var/www/vhosts/scottsdalemint.com/logs/
Monitoring Metrics
Track these key metrics:
- Request rate (requests/second)
- Average response time
- Error rate (4xx, 5xx responses)
- Memory usage
- CPU utilization
- Disk I/O (image uploads)
Maintenance
Updating the Application
-
Build new version locally:
dotnet publish -c Release -p:Platform=x86 -o publish -
Stop IIS application pool in Plesk
-
Backup current
appsettings.jsonfile -
Replace all files in httpdocs (except
appsettings.json) -
Restore
appsettings.jsonor merge new settings -
Restart application pool
-
Verify health endpoint and test decode
Backup Strategy
Weekly Backups:
- Application files (via Plesk backup)
appsettings.jsonconfiguration- Deployment scripts
Daily Monitoring:
- Health endpoint checks
- Error log review
- Performance metrics
Support Contacts
For issues or questions:
- Developer: Rhino Group Development Team
- Server: Rhino Group DevOps
- API Access: Contact client administrator
Technical Details
Native Library Integration
The application uses P/Invoke to call native C functions from PeltaDecoder.dll:
Key Functions:
qr_000000000000()- Master key derivationqr_decOpen()- Initialize decoder with grayscale imageqr_decAnalyze()- Scan image for QR codeqr_decGetMessage()- Extract overt messageqr_decGetIndependentKeyed()- Extract covert messageqr_decClose()- Free decoder resources
Memory Management:
- Decoder handles allocated via
qr_decOpen() - Must call
qr_decClose()in finally block to prevent leaks - Grayscale byte array passed by reference to native code
- Unsafe pointer operations handled by P/Invoke layer
Image Processing Pipeline
- Upload: Multipart form data received
- Validation: Check content type and file signature
- Load: ImageSharp loads image into Rgba32 format
- Grayscale: Mutate operation converts to grayscale
- Extract: ProcessPixelRows extracts byte data row-major
- Decode: Pass byte array to native decoder
- Cleanup: Dispose image resources
Error Handling
The application uses try-catch blocks at multiple levels:
- Controller Level: HTTP exception handling
- Service Level: Decoder operation error handling
- Native Level: P/Invoke error codes
- Image Level: ImageSharp exception handling
All errors are logged with context and returned as structured JSON responses.
Repository Information
- Repository:
Z:\Repos\pelta-api - Solution: PeltaApi.sln
- Project: PeltaApi/PeltaApi.csproj
- Target Framework: net10.0
- Platform: x86
Project Structure
pelta-api/
├── PeltaApi/
│ ├── Controllers/
│ ├── Middleware/
│ ├── Services/
│ ├── native/
│ │ └── PeltaDecoder.dll
│ ├── wwwroot/
│ │ └── index.html
│ ├── Program.cs
│ ├── appsettings.json
│ ├── PeltaApi.csproj
│ └── Agents.MD
├── PeltaApi.sln
└── README.md
Version History
Version 1.0.0 (June 2026)
- Initial production release
- QR code decoding with overt/covert message extraction
- API key authentication
- IP-restricted test client
- CORS support
- x86 platform targeting
- Health check endpoint
- Comprehensive error handling
- Production deployment on Plesk/IIS
Related Documentation
- Scottsdale Mint Middleware - CertiLock image API and backend services
- Scottsdale Mint Mobile App - CertiLock® verification mobile application
- Scottsdale Mint Retail - Primary e-commerce storefront
License
Proprietary software owned by Scottsdale Mint / Rhino Group.
PeltaDecoder.dll: Proprietary QR decoder library with covert message support
SixLabors.ImageSharp: MIT License (image processing)
ASP.NET Core: MIT License (web framework)