Skip to main content

Catalog Module

Manage product databases, generate print-ready catalogs with multi-language support, Shopify sync, and professional PDF output.

Base URL: https://app.proign.com/[tenant]/catalog/api

Overview

The Catalog module provides a centralized product database with professional PDF catalog generation:

  • Product database with rich metadata, translations, and images
  • Professional PDF catalog generation via queue-based rendering
  • Multi-language support for global markets
  • Real-time Shopify product sync via webhooks
  • Category and collection management with hierarchical structure
  • Product badges for quality marks, programs, and warnings
  • Customizable page templates and content blocks
  • Auto-layout engine for rapid catalog assembly

Key Features

Product Database

Centralized product information management with full multilingual support:

  • Products - SKU, translations (name, description, features per language), images, pricing, and specifications
  • Categories - Hierarchical product categorization with parent-child relationships
  • Badges - Quality marks, program icons, and warning labels attached to products
  • Compatibility - Cross-reference arrays linking products to compatible models
  • Media - Primary image plus additional image gallery, stored in R2

Product Schema

Each product stores the following fields. The translations field holds all localizable content keyed by language code:

{
  "id": "a1b2c3d4e5f6...",
  "tenant_id": "CANIKUSA",
  "sku": "TP9-SFX-001",
  "status": "active",              // "draft" | "active" | "archived"
  "translations": {
    "en": {
      "name": "TP9 SFx Rival-S",
      "description": "Competition-ready 9mm with...",
      "features": ["Match-grade barrel", "Fiber optic sights"]
    },
    "tr": {
      "name": "TP9 SFx Rival-S",
      "description": "Yarişma tipi 9mm...",
      "features": ["Match sınıfı namlu", "Fiber optik nişangah"]
    }
  },
  "primary_image_r2_key": "catalog/CANIKUSA/products/tp9-sfx.jpg",
  "image_r2_keys": [
    "catalog/CANIKUSA/products/tp9-sfx-angle.jpg",
    "catalog/CANIKUSA/products/tp9-sfx-detail.jpg"
  ],
  "category_id": "cat_pistols",
  "badge_ids": ["badge_quality", "badge_new"],
  "warning_text": "Read manual before use",
  "color_hex": "#1a1a1a",
  "compatibility": ["TP9 Series", "METE Series"],
  "shopify_product_id": "8012345678901",
  "shopify_variant_id": "4412345678901",
  "shopify_synced_at": "2026-02-20T14:30:00.000Z",
  "price_cents": 59999,
  "currency": "USD",
  "sort_order": 10,
  "created_at": "2026-01-15T10:00:00.000Z",
  "updated_at": "2026-02-20T14:30:00.000Z"
}

PDF Template Customization

Catalogs are assembled from structured pages, each with a type and optional template. The system supports five page types:

Page Types

TypeDescription
coverFull-page cover with branding, title, and background image
tocAuto-generated table of contents from category pages
category_dividerSection divider page introducing a product category
product_gridProduct layout page with assigned products and content blocks
customFree-form page built entirely from content blocks

Content Blocks

Each page can contain ordered content blocks that define the page layout. Blocks are reorderable and each stores its own content payload:

Block TypeUsage
headingSection titles and subtitles
paragraphRich text content
imageProduct photos, lifestyle images, diagrams
tableSpecification tables, comparison charts
listFeature lists, bullet points
warning_boxSafety notices, regulatory callouts
qr_codeQR codes linking to digital resources
spacerVertical spacing between blocks
dividerHorizontal rule separators

Custom Branding

Each catalog stores a branding object and a cover_config object for full visual customization:

  • Branding - Logo, primary color, accent color, footer text, fonts
  • Cover Config - Background image, title placement, subtitle, year badge
  • Page Format - A4, Letter, or A5 in portrait or landscape orientation
  • Custom Dimensions - Override standard formats with exact width/height (mm)
  • Document Type - Catalog, manual, packing list, or card layout

Page Templates

Reusable templates define default layouts for page types. Templates store their layout as a JSON configuration and can be marked as the default for their type:

// Example template layout (product_grid)
{
  "name": "2-Column Product Grid",
  "page_type": "product_grid",
  "layout_json": {
    "columns": 2,
    "showPrice": true,
    "showSku": true,
    "showDescription": true,
    "imagePosition": "top",
    "badgePlacement": "top-right"
  },
  "is_default": 1
}

Auto-Layout

The auto-layout engine generates catalog pages from a list of category IDs. It creates category dividers and product grid pages automatically, including a table of contents if enabled on the catalog:

POST /api/[tenant]/catalog/catalogs/[id]/auto-layout
Content-Type: application/json

{
  "categoryIds": ["cat_pistols", "cat_accessories", "cat_optics"]
}

Shopify Sync

The catalog module integrates with Shopify via HMAC-validated webhooks. Product data flows from Shopify into the catalog in real time:

Sync Flow

  1. Connect your Shopify store via Settings (store domain, access token, client secret)
  2. Register webhooks for products/create, products/update, and products/delete
  3. Shopify sends HMAC-signed payloads to /api/[tenant]/catalog/shopify/webhook
  4. The module validates the HMAC signature, identifies the tenant from the shop domain header, and upserts the product
  5. Any catalogs containing the affected product are automatically queued for PDF regeneration

Product Matching

Products are matched using the Shopify product ID. On create or update:

  • If a product with the same shopify_product_id exists, it is updated in place (upsert)
  • If no match exists, a new product record is created with status mapped from Shopify (active stays active, everything else becomes draft)
  • SKU is pulled from the first variant; price is converted to cents
  • Product title and HTML-stripped description are stored in the en translation
  • Image URLs from Shopify are stored in image_r2_keys until they are downloaded to R2

Conflict Resolution

The webhook-based sync follows last-write-wins semantics:

  • Shopify is treated as the source of truth for synced fields (title, description, price, SKU, status)
  • Local-only fields (badges, compatibility, warning text, category assignment, color) are preserved during sync
  • The shopify_synced_at timestamp records when the last sync occurred
  • On product delete, the local product is removed and affected catalogs are regenerated

Security

Shopify credentials (access token and client secret) are encrypted at rest using AES encryption with tenant-scoped keys. HMAC validation ensures webhook authenticity. The tenant is identified from the x-shopify-shop-domain header, preventing cross-tenant data leaks.

Multi-Language Support

Translations are stored directly on each product in a JSON map keyed by ISO language code. Each language entry contains the localized name, description, and feature list:

"translations": {
  "en": {
    "name": "Elite Combat Executive",
    "description": "Premium carry pistol with...",
    "features": ["SAI barrel", "Tungsten guide rod"]
  },
  "de": {
    "name": "Elite Combat Executive",
    "description": "Premium-Tragepistole mit...",
    "features": ["SAI-Lauf", "Wolfram-Führungsstange"]
  },
  "fr": {
    "name": "Elite Combat Executive",
    "description": "Pistolet de port premium avec...",
    "features": ["Canon SAI", "Tige de guidage en tungstène"]
  }
}

Localized Catalogs

Each catalog has a language and optional region field. When generating a PDF, the system pulls translations matching the catalog language. To produce the same catalog in multiple languages:

  1. Create the master catalog in your default language with all pages and products
  2. Use the Duplicate Catalog endpoint with a different language and region
  3. The duplicate copies all pages, products, and blocks but renders using the target language translations
  4. Each localized catalog can be independently customized after duplication
POST /api/[tenant]/catalog/catalogs/[id]/duplicate
Content-Type: application/json

{
  "name": "2026 Produktkatalog",
  "slug": "2026-catalog-de",
  "language": "de",
  "region": "DACH"
}

API Endpoints

Products

GET    /api/[tenant]/catalog/products          # List products (filterable)
GET    /api/[tenant]/catalog/products/[id]     # Get product details
POST   /api/[tenant]/catalog/products          # Create product
PATCH  /api/[tenant]/catalog/products/[id]     # Update product

Query params: categoryId, status (draft|active|archived), search, limit (1-100, default 50), offset (default 0).

Categories

GET    /api/[tenant]/catalog/categories        # List categories
POST   /api/[tenant]/catalog/categories        # Create category
PATCH  /api/[tenant]/catalog/categories/[id]   # Update category
DELETE /api/[tenant]/catalog/categories/[id]   # Delete category

Badges

GET    /api/[tenant]/catalog/badges             # List badges
POST   /api/[tenant]/catalog/badges             # Create badge
PATCH  /api/[tenant]/catalog/badges/[id]        # Update badge
DELETE /api/[tenant]/catalog/badges/[id]        # Delete badge

Catalogs

GET    /api/[tenant]/catalog/catalogs           # List catalogs
POST   /api/[tenant]/catalog/catalogs           # Create catalog
GET    /api/[tenant]/catalog/catalogs/[id]      # Get catalog details
PATCH  /api/[tenant]/catalog/catalogs/[id]      # Update catalog
DELETE /api/[tenant]/catalog/catalogs/[id]      # Delete catalog
POST   /api/[tenant]/catalog/catalogs/[id]/duplicate    # Duplicate catalog
POST   /api/[tenant]/catalog/catalogs/[id]/auto-layout  # Auto-generate pages

Catalog Pages

GET    /api/[tenant]/catalog/catalogs/[id]/pages              # List pages
POST   /api/[tenant]/catalog/catalogs/[id]/pages              # Create page
GET    /api/[tenant]/catalog/catalogs/[id]/pages/[pageId]     # Get page
PATCH  /api/[tenant]/catalog/catalogs/[id]/pages/[pageId]     # Update page
DELETE /api/[tenant]/catalog/catalogs/[id]/pages/[pageId]     # Delete page
POST   /api/[tenant]/catalog/catalogs/[id]/pages/reorder      # Reorder pages

Page Products

GET    /api/.../pages/[pageId]/products            # List page products
POST   /api/.../pages/[pageId]/products            # Add product to page
PATCH  /api/.../pages/[pageId]/products/[ppId]     # Update page product
DELETE /api/.../pages/[pageId]/products/[ppId]     # Remove product from page
POST   /api/.../pages/[pageId]/products/reorder    # Reorder products

Content Blocks

GET    /api/.../pages/[pageId]/blocks               # List blocks
POST   /api/.../pages/[pageId]/blocks               # Create block
PATCH  /api/.../pages/[pageId]/blocks/[blockId]     # Update block
DELETE /api/.../pages/[pageId]/blocks/[blockId]     # Delete block
POST   /api/.../pages/[pageId]/blocks/reorder       # Reorder blocks

Templates

GET    /api/[tenant]/catalog/templates           # List templates
POST   /api/[tenant]/catalog/templates           # Create template
PATCH  /api/[tenant]/catalog/templates/[id]      # Update template
DELETE /api/[tenant]/catalog/templates/[id]      # Delete template

PDF Generation

POST   /api/[tenant]/catalog/generate           # Queue catalog PDF generation
GET    /api/[tenant]/catalog/generate/[jobId]  # Check generation status

Settings and Webhooks

GET    /api/[tenant]/catalog/settings            # Get catalog settings
PUT    /api/[tenant]/catalog/settings            # Update settings
POST   /api/[tenant]/catalog/shopify/webhook     # Shopify webhook receiver

Request Examples

Create Product

POST /api/[tenant]/catalog/products
Content-Type: application/json
Authorization: Bearer <jwt>

{
  "sku": "TP9-METE-SFT",
  "status": "active",
  "translations": {
    "en": {
      "name": "METE SFT",
      "description": "Full-size tactical pistol...",
      "features": ["18+1 capacity", "Optic-ready slide"]
    }
  },
  "category_id": "cat_pistols",
  "badge_ids": ["badge_new"],
  "price_cents": 47999,
  "currency": "USD",
  "compatibility": ["METE Series"]
}

Response (201):

{
  "data": {
    "id": "a1b2c3d4e5f6...",
    "tenant_id": "CANIKUSA",
    "sku": "TP9-METE-SFT",
    "status": "active",
    "translations": { "en": { ... } },
    "category_id": "cat_pistols",
    "badge_ids": ["badge_new"],
    "price_cents": 47999,
    "currency": "USD",
    "created_at": "2026-02-21T10:00:00.000Z",
    "updated_at": "2026-02-21T10:00:00.000Z"
  }
}

Create Catalog

POST /api/[tenant]/catalog/catalogs
Content-Type: application/json
Authorization: Bearer <jwt>

{
  "name": "2026 Product Catalog",
  "slug": "2026-catalog-en",
  "language": "en",
  "region": "US",
  "page_format": "letter",
  "orientation": "portrait",
  "document_type": "catalog",
  "toc_enabled": 1,
  "branding": {
    "logo_r2_key": "catalog/CANIKUSA/logo.png",
    "primary_color": "#1a1a1a",
    "accent_color": "#c8102e",
    "footer_text": "CANIK USA 2026"
  },
  "cover_config": {
    "background_image": "catalog/CANIKUSA/cover-bg.jpg",
    "title": "2026 PRODUCT CATALOG",
    "subtitle": "Innovation Meets Precision"
  }
}

Add Content Block to Page

POST /api/[tenant]/catalog/catalogs/[id]/pages/[pageId]/blocks
Content-Type: application/json
Authorization: Bearer <jwt>

{
  "block_type": "warning_box",
  "sort_order": 3,
  "content": {
    "text": "Always follow firearm safety rules.",
    "icon": "warning",
    "style": "danger"
  }
}

Generate PDF

POST /api/[tenant]/catalog/generate
Content-Type: application/json
Authorization: Bearer <jwt>

{
  "catalogId": "abc123def456"
}

Response (202): The job is queued for processing. Poll the status endpoint to track progress:

{
  "data": {
    "jobId": "output_xyz789",
    "status": "queued",
    "version_number": 3,
    "trigger_source": "manual"
  }
}

// Poll: GET /api/[tenant]/catalog/generate/output_xyz789
// -> { "data": { "status": "completed", "r2_key": "...", "page_count": 48 } }

Configuration

Configure the catalog module via PUT /api/[tenant]/catalog/settings:

  • Shopify Store Domain - The mystore.myshopify.com domain for webhook matching
  • Shopify Access Token - API access token for product sync (encrypted at rest)
  • Shopify Client Secret - Used for HMAC webhook validation (encrypted at rest)
  • Default Language - Fallback language code for new catalogs (e.g., en)
  • Default Region - Default market region for new catalogs (e.g., US)
PUT /api/[tenant]/catalog/settings
Content-Type: application/json
Authorization: Bearer <jwt>

{
  "shopify_store_domain": "canikusa.myshopify.com",
  "shopify_access_token": "shpat_xxxxxxxxxxxx",
  "shopify_client_secret": "shpss_xxxxxxxxxxxx",
  "default_language": "en",
  "default_region": "US"
}

Sensitive fields are returned as **** in GET responses. Shopify credentials are AES-encrypted with tenant-scoped keys before storage.

Related