Files
cannaiq/backend/API_USAGE.md
Kelly d91c55a344 feat: Add stale process monitor, users route, landing page, archive old scripts
- Add backend stale process monitoring API (/api/stale-processes)
- Add users management route
- Add frontend landing page and stale process monitor UI on /scraper-tools
- Move old development scripts to backend/archive/
- Update frontend build with new features

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 04:07:31 -07:00

11 KiB

Dutchie Analytics API - Usage Guide

Base URL

http://localhost:3010/api

Authentication

All endpoints require JWT authentication via Bearer token:

Authorization: Bearer YOUR_JWT_TOKEN

Products API

Get Products with Filtering, Sorting & Field Selection

Endpoint: GET /products

Basic Usage

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&limit=10"

Field Selection (Reduce Payload Size)

Only return specific fields to reduce bandwidth and improve performance:

# Only get essential fields
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&fields=id,name,price,brand,in_stock"

# Get fields needed for product cards
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&fields=id,name,price,brand,thc_percentage,image_url_full,in_stock"

Advanced Filtering

Filter by Category:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&category_id=5"

Filter by Stock Status:

# Only in-stock products
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&in_stock=true"

Search Products:

# Search in name, brand, and description
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?search=blue+dream"

Filter by Brand:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&brand=Cresco"

Filter by Price Range:

# Products between $20 and $50
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&min_price=20&max_price=50"

Filter by THC Percentage:

# High THC products (>20%)
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&min_thc=20"

Filter by Strain Type:

# Get only indica strains
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&strain_type=indica"

Sorting

Sort by Price:

# Lowest price first
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&sort_by=price&sort_order=asc"

# Highest price first
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&sort_by=price&sort_order=desc"

Sort by THC:

# Highest THC first
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&sort_by=thc_percentage&sort_order=desc"

Sort by Name:

# Alphabetical
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&sort_by=name&sort_order=asc"

Available Sort Fields:

  • id
  • name
  • brand
  • price
  • thc_percentage
  • cbd_percentage
  • last_seen_at
  • created_at

Pagination

# Get first page (50 items)
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&limit=50&offset=0"

# Get second page
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?store_id=1&limit=50&offset=50"

Complex Query Example

Get affordable indica products sorted by THC:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products?\
store_id=1&\
strain_type=indica&\
max_price=40&\
min_thc=15&\
in_stock=true&\
sort_by=thc_percentage&\
sort_order=desc&\
limit=20&\
fields=id,name,brand,price,thc_percentage,strain_type,image_url_full"

Response Format

{
  "products": [
    {
      "id": 123,
      "name": "Blue Dream 3.5g",
      "brand": "Cresco Labs",
      "price": 45.00,
      "thc_percentage": 24.5,
      "cbd_percentage": 0.5,
      "strain_type": "hybrid",
      "in_stock": true,
      "image_url_full": "http://localhost:9020/dutchie/products/123/full.jpg",
      "thumbnail_url": "http://localhost:9020/dutchie/products/123/thumb.jpg",
      "medium_url": "http://localhost:9020/dutchie/products/123/medium.jpg",
      "store_name": "Curaleaf - Phoenix",
      "category_name": "Flower"
    }
  ],
  "total": 145,
  "limit": 50,
  "offset": 0,
  "filters": {
    "store_id": "1",
    "category_id": null,
    "in_stock": "true",
    "search": null,
    "brand": null,
    "min_price": null,
    "max_price": null,
    "min_thc": null,
    "max_thc": null,
    "strain_type": null,
    "sort_by": "last_seen_at",
    "sort_order": "DESC"
  }
}

Get Single Product

Endpoint: GET /products/:id

# Full product data
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products/123"

# With field selection
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products/123?fields=id,name,price,description,image_url_full"

Get Available Brands (Meta Endpoint)

Endpoint: GET /products/meta/brands

Get list of all brands for filter dropdowns:

# All brands
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products/meta/brands"

# Brands for specific store
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products/meta/brands?store_id=1"

Response:

{
  "brands": [
    "Cresco Labs",
    "Select",
    "Timeless",
    "Canamo",
    "Item 9"
  ]
}

Get Price Range (Meta Endpoint)

Endpoint: GET /products/meta/price-range

Get min/max/avg prices for filter sliders:

# All products
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products/meta/price-range"

# For specific store
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/products/meta/price-range?store_id=1"

Response:

{
  "min_price": 15.00,
  "max_price": 120.00,
  "avg_price": 42.50
}

Stores API

Get All Stores

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/stores"

Get Single Store

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/stores/1"

Get Store Brands

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/stores/1/brands"

Get Store Specials

# Today's specials
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/stores/1/specials"

# Specials for specific date
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/stores/1/specials?date=2025-01-15"

Trigger Store Scrape (Admin)

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"parallel": 3}' \
  "http://localhost:3010/api/stores/1/scrape"

Categories API

Get Categories (Flat List)

# All categories
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/categories"

# For specific store
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/categories?store_id=1"

Get Category Tree (Hierarchical)

curl -H "Authorization: Bearer YOUR_TOKEN" \
  "http://localhost:3010/api/categories/tree?store_id=1"

Real-World Usage Examples

Build a Product Filter UI

  1. Get filter options:
// Get brands for dropdown
const brands = await fetch('/api/products/meta/brands?store_id=1');

// Get price range for slider
const priceRange = await fetch('/api/products/meta/price-range?store_id=1');

// Get categories for checkboxes
const categories = await fetch('/api/categories?store_id=1');
  1. Fetch filtered products:
const params = new URLSearchParams({
  store_id: '1',
  brand: selectedBrand,
  min_price: minPrice,
  max_price: maxPrice,
  category_id: selectedCategory,
  in_stock: 'true',
  sort_by: 'price',
  sort_order: 'asc',
  fields: 'id,name,price,brand,thc_percentage,image_url_full,in_stock'
});

const products = await fetch(`/api/products?${params}`);

Build a WordPress Product Grid

// Efficient field selection for grid display
$fields = 'id,name,price,brand,thc_percentage,image_url_full,in_stock';

$response = wp_remote_get(
  "http://localhost:3010/api/products?store_id=1&in_stock=true&limit=12&fields=$fields",
  ['headers' => ['Authorization' => 'Bearer ' . $token]]
);

$data = json_decode(wp_remote_retrieve_body($response));
foreach ($data->products as $product) {
  // Only contains requested fields = smaller payload
  echo render_product_card($product);
}

Build Price Comparison Tool

// Get cheapest products across all stores
const cheapProducts = await fetch(
  '/api/products?in_stock=true&sort_by=price&sort_order=asc&limit=50&fields=id,name,price,store_name'
);

// Get highest THC products
const strongProducts = await fetch(
  '/api/products?in_stock=true&min_thc=25&sort_by=thc_percentage&sort_order=desc&limit=50'
);

Performance Tips

  1. Use Field Selection: Only request fields you need to reduce bandwidth
  2. Pagination: Use reasonable limit values (50-100)
  3. Caching: Cache brand lists, price ranges, and category data
  4. Combine Filters: Use multiple filters to reduce result set size
  5. Index Optimization: The API uses indexed fields for filtering

Rate Limits

  • Standard users: 100 requests/minute
  • Admin users: 500 requests/minute
  • Exceeded limits return 429 Too Many Requests

Error Responses

{
  "error": "Failed to fetch products"
}

HTTP Status Codes:

  • 200 - Success
  • 400 - Bad Request
  • 401 - Unauthorized
  • 404 - Not Found
  • 429 - Too Many Requests
  • 500 - Server Error

WordPress Plugin

Plugin Download

The WordPress plugin is available for download from the admin dashboard landing page.

Download URL Pattern:

/downloads/cb-wpmenu-{version}.zip

Example: /downloads/cb-wpmenu-1.5.1.zip

Building the Plugin

Use the build script to create a new plugin zip for distribution:

cd wordpress-plugin
./build-plugin.sh

The build script will:

  1. Extract the version from crawlsy-menus.php
  2. Create a zip file named cb-wpmenu-{version}.zip
  3. Output the file to backend/public/downloads/
  4. Display a reminder to update the landing page

Release Checklist

When releasing a new plugin version:

  1. Update the version in wordpress-plugin/crawlsy-menus.php:

    * Version: 1.5.2
    
  2. Build the plugin:

    cd wordpress-plugin
    ./build-plugin.sh
    
  3. Update the landing page (frontend/src/pages/LandingPage.tsx):

    • Update download URLs: href="/downloads/cb-wpmenu-{version}.zip"
    • Update button text: Download Plugin v{version}
  4. Deploy:

    • Local: The zip is served from backend/public/downloads/
    • Remote: Commit and push changes, then deploy

Naming Convention

Component Format Example
Plugin file crawlsy-menus.php -
Zip file cb-wpmenu-{version}.zip cb-wpmenu-1.5.1.zip
Download URL /downloads/cb-wpmenu-{version}.zip /downloads/cb-wpmenu-1.5.1.zip
Shortcodes [cb_products], [cb_product] [cb_products limit="12"]