Normalization API
Send raw POS product data from any dispensary menu system and receive enriched, standardized product records using CANN's normalization pipeline.
Base URL: https://api.cannmenus.com/v1 — Uses the same authentication token as the CannMenus Partner API.
What It Does
You send raw product data from any POS or menu system, and we return:
Category & Subcategory
Raw product types mapped to CANN's canonical taxonomy — 9 categories, 100+ subcategories.
Brand Matching
Fuzzy-matched against CANN's database of 10,000+ cannabis brands with confidence scores.
Potency & Weight
THC/CBD percentages and milligrams extracted. Weights normalized to grams from any format.
UUID/SKU Matching
Cross-referenced against CANN's existing product database for identity resolution.
Two Ways to Use It
Batch API (synchronous)
Send up to 500 products per request and get results immediately:
POST /v1/normalize/batch
Best for: Real-time integrations, small-to-medium datasets, testing.
File Upload (asynchronous)
Upload a CSV or JSON file (up to 50MB) and poll for results:
POST /v1/files/upload
GET /v1/files/{job_id}/status
GET /v1/files/{job_id}/results
Best for: Large menu exports, bulk processing, one-time migrations.
Quick Example
Normalize a single Dutchie product:
curl -X POST "https://api.cannmenus.com/v1/normalize/batch" \
-H "X-Token: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "dutchie",
"products": [
{
"data": {
"Name": "Happy Valley - Dog Patch - 3.5g",
"type": "Flower",
"THCContent": {"unit": "PERCENTAGE", "range": [24.0, 24.0]},
"Prices": [52.00],
"brand": "Happy Valley",
"strainType": "Hybrid"
},
"state": "MA",
"recreational": true
}
],
"match_existing": true
}'
Example Response
{
"provider": "dutchie",
"total_input": 1,
"total_output": 1,
"total_matched": 1,
"total_errors": 0,
"processing_time_ms": 142.5,
"products": [
{
"uuid": "a1b2c3d4-...",
"sku": "XkR9mN2pQw7vL4bT",
"match_tier": "secondary",
"display_product_name": "Dog Patch",
"raw_product_name": "Happy Valley - Dog Patch - 3.5g",
"cann_product_category": "Flower",
"display_strain_type": "Hybrid",
"display_weight": "3.5g",
"display_potency_list": ["24% THC"],
"brand_name": "Happy Valley",
"brand_fuzzy_match_score": 0.95,
"total_weight_in_grams": 3.5,
"current_price": 52.0,
"state": "MA",
"in_stock": true,
"recreational": true
}
],
"errors": []
}
Supported POS Providers
We have dedicated normalizers for specific providers with full field mapping. For all other POS systems, use "provider": "generic".
| Provider | Identifier | Type |
|---|---|---|
| Dutchie | dutchie | Dedicated normalizer |
| Generic (any POS) | generic | Heuristic-based |
Use the Providers endpoint to get the current list programmatically.
Additional dedicated normalizers (iHeartJane, Leafly, Weedmaps, Buddi, Trulieve) are being activated. In the meantime, use "provider": "generic" for these — the generic normalizer handles most POS formats well.
Authentication
All requests require the X-Token header — the same token you use for the CannMenus Partner API. See Authentication for details.
curl -X POST "https://api.cannmenus.com/v1/normalize/batch" \
-H "X-Token: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... }'
Getting Started
- Use your existing CannMenus API token (or contact us to get one)
- Start small — send 10-20 products to verify the output matches your expectations
- Enable matching — use
"match_existing": trueto get UUID/SKU cross-references - Scale up — move to file uploads for larger datasets
API Reference
Full interactive API documentation is available at the Normalization API Redoc.
Next Steps
- Batch Normalization — Full endpoint reference for synchronous normalization
- File Upload — Async file processing workflow
- Providers — List of supported POS providers
- Categories — CANN's canonical product categories
- Tags — Product tags and attributes
