API Documentation

RESTful API for SAGE promotional products middleware.

Authentication

All API endpoints require authentication via the X-API-Key header.

curl -H "X-API-Key: your-api-key" https://mam-one.vercel.app/inventory/12345

Permission Levels

Level Access
inventory Only /inventory/* endpoints
full All API endpoints

Rate Limiting

API requests are rate limited to ensure fair usage and system stability. Limits vary by endpoint type.

Rate Limits by Endpoint

Endpoint Type Limit
Standard endpoints 100 requests/minute
Search endpoints 60 requests/minute
Export endpoints 10 requests/minute
Sync endpoints 5 requests/minute

Response Headers

Rate limit information is included in response headers:

X-RateLimit-Limit Maximum requests allowed in the window
X-RateLimit-Remaining Requests remaining in current window
X-RateLimit-Reset Unix timestamp when the window resets

Note: When rate limited, you'll receive a 429 Too Many Requests response. Wait until the reset time before retrying.

Inventory

Real-time inventory lookups via SAGE Module 107. FREE - no credits required.

GET /inventory/{sage_prod_eid}

Get inventory by SAGE product ID

Response

{
  "sage_prod_eid": 305466229,
  "found": true,
  "total_on_hand": 9993,
  "is_unlimited": false,
  "last_updated": "2024-12-14T10:30:00Z",
  "skus": [
    {
      "attributes": [
        {"type_id": 10, "name": "Color", "value": "Black"}
      ],
      "on_hand": 500,
      "is_unlimited": false,
      "on_order": 0,
      "expected_date": null
    }
  ],
  "cached": false
}
GET /inventory/by-item/{supplier_sage_id}/{item_num}

Get inventory by supplier and item number (more reliable)

Example

GET /inventory/by-item/50018/1328
POST /inventory/bulk

Get inventory for multiple products (up to 100)

Request Body

{
  "sage_prod_eids": [305466229, 305466230, 305466231]
}

Products

Access cached product data from the database. Requires full permission.

GET /products

List products with pagination and filtering

Query Parameters

supplier_id Filter by supplier (internal ID)
product_type Filter by product type
category_text Filter by SAGE category
search Search in name and description
published_only Only show published products (default: true)
include_facets Include facet counts for filtering (default: false)
page Page number (default: 1)
per_page Items per page (default: 50, max: 100)

Response Fields

Each product in the response includes:

  • retail_min_price - Calculated retail price (minimum)
  • retail_max_price - Calculated retail price (maximum)
  • is_visible - Whether product is visible to customers
GET /products/{id}

Get product by internal database ID

Returns full product detail including retail pricing tiers when available.

GET /products/by-sage-id/{sage_prod_eid}

Get product by SAGE product ID

GET /products/by-slug/{slug}

Get product by URL slug

GET /products/categories

List available product categories from SAGE

GET /products/stats/summary

Get database statistics

Parts Pricing & Product Images

Multi-part products (awards in multiple sizes, mugs in multiple colors) include per-part pricing and images via the parts_pricing field. Simple products have parts_pricing: null.

When parts_pricing is present

Products from PromoStandards suppliers (e.g., SRG) that bundle multiple SKUs under one product ID. Each part has its own pricing tiers and images.

{
  "id": 53975,
  "item_num": "ARG202-HG",
  "name": "Fantasia Award on Hancock Base - Green",
  "primary_image_url": "https://...",
  "all_images": [{"url": "...", "caption": "", "hasLogo": false}],

  "parts_pricing": {
    "default_part_id": "ARG2021-HG",
    "parts": [
      {
        "part_id": "ARG2021-HG",
        "description": "Fantasia Award on Hancock Green - 3\" Diam",
        "tiers": [
          {"qty": 1, "prc": 82.5, "net": 41.25},
          {"qty": 7, "prc": 81.0, "net": 40.5}
        ],
        "images": [
          {"url": "https://.../arg2021-hg-primary.jpg", "caption": ""},
          {"url": "https://.../arg2021-hg-blank.jpg", "caption": ""}
        ]
      },
      {
        "part_id": "ARG2022-HG",
        "description": "Fantasia Award on Hancock Green - 4\" Diam",
        "tiers": [{"qty": 1, "prc": 125.5, "net": 62.75}],
        "images": [
          {"url": "https://.../arg2022-hg-primary.jpg", "caption": ""},
          {"url": "https://.../arg2022-hg-blank.jpg", "caption": ""}
        ]
      }
    ]
  }
}

Key Fields

parts_pricing.default_part_id The default/first part ID. Use to pre-select a variation.
parts[].part_id Unique SKU for this part (e.g., size or color variant)
parts[].description Full description including differentiating attribute (size, color)
parts[].tiers Quantity-based pricing for this specific part. Use tiers[0].prc for qty=1 price.
parts[].images Part-specific images (typically Primary + Blank). Use images[0].url as the variation image.

Per-Part Images vs Product Images

Field Scope Use For
primary_image_url Product-level Main product image / featured image
all_images Product-level Gallery / all product images (includes part images)
parts[].images Part-specific Variation image. Typically 2 per part: Primary (with decoration) and Blank (undecorated).

Integration tip: Use part.images[0].url as the variation image, falling back to primary_image_url if the part has no images.

WooCommerce Variable Product Mapping

Map multi-part products to WooCommerce variable products:

WooCommerce MAM Source
Product type variable when parts_pricing exists, else simple
Parent SKU item_num
Variation SKU part.part_id
Variation price part.tiers[0].prc (qty=1 price)
Variation image part.images[0].url, fallback to primary_image_url
Attribute name "Option" (generic — parts can be sizes, colors, materials)
Default variation Match default_part_id

Collections

Award Maven's curated product collections. Requires full permission.

GET /collections

List all collections

Query Parameters

active_only Only active collections (default: true)
featured_only Only featured collections (default: false)
parent_id Filter by parent collection ID
enforce_schedule Apply date-based scheduling (default: true)
published_only Only published collections (default: true)
GET /collections/by-slug/{slug}

Get collection by URL slug

GET /collections/{id}

Get collection by internal ID

GET /collections/{id}/products

Get products in a collection with pagination

Query Parameters

page Page number (default: 1)
per_page Items per page (default: 50, max: 100)

Families

Product variant families (same product in different sizes/colors). Requires full permission.

GET /families

List all product families

Query Parameters

page Page number (default: 1)
per_page Items per page (default: 50, max: 100)
GET /families/by-slug/{slug}

Get family by URL slug

GET /families/{id}

Get family by internal ID (includes all variant products)

Response includes:

  • display_mode - How variants display: separate_pages, single_page, cross_links
  • variant_axes - Variation dimensions (e.g., ["size", "color"])
  • products - Array of variant products in the family

Export

Export products to WooCommerce-compatible CSV. Requires full permission.

Standard Export

GET /export/csv

Download WooCommerce CSV export (1 variable + 1 variation per product)

Query Parameters

supplier_sage_id Filter by supplier SAGE ID
markup_percent Markup percentage to apply (default: 0, since prc is already customer price)
use_prc Use customer price (prc). Set false to use cost (net). Default: true
round_to_quarter Round final price to nearest $0.25. Default: false
GET /export/preview

Preview export without downloading (JSON response)

Variable Export

Groups products by base name as WooCommerce variable products with size/color variations.

GET /export/variable-csv

Download grouped variable products CSV

Same parameters as /export/csv

GET /export/variable-preview

Preview variable product grouping

GET /export/variable-stats

Get variable export statistics

Pricing Formula

retail_price = base_price × (1 + markup_percent / 100)

where base_price = prc (default, customer price) or net (if use_prc=false)
then optionally round to nearest $0.25

Default behavior: Uses prc (customer price) directly with 0% markup.

Example: To add 5% to list price: markup_percent=5

Suppliers

Access supplier data. Requires full permission.

GET /suppliers/search

Search SAGE for suppliers by name. Costs 1 credit per call.

Query Parameters

q Search query (required)
GET /suppliers/export

Export suppliers from database (no SAGE credit cost)

Query Parameters

updated_since ISO 8601 timestamp for incremental sync
active_only Only active suppliers (default: true)
page Page number (default: 1)
page_size Items per page (default: 50, max: 100)

Sync

Sync products from SAGE suppliers. Requires full permission. Costs credits per product.

POST /sync/preview/{supplier_sage_id}

Preview products without saving (1 credit)

POST /sync/supplier/{supplier_sage_id}

Sync products to database

GET /sync/{sync_id}

Check sync status

Error Handling

All errors return JSON with an error message:

{
  "detail": "Invalid API key"
}

HTTP Status Codes

200 Success
401 Missing or invalid API key
403 Insufficient permissions
404 Resource not found
429 Rate limit exceeded. Check X-RateLimit-* headers.
500 Internal server error