If you’re running a headless storefront — Next.js, Nuxt, Astro, Remix, or any custom front end — minipim serves as the product data layer via its REST API and webhook event bus. Your storefront fetches product data from minipim (or from a CDN/ISR cache backed by minipim), and your build or edge layer stays up to date via webhooks when products change.

The model

minipim catalog

    ├── REST API  →  your storefront (SSR, ISR, or static build)
    └── Webhooks  →  your cache invalidation / build trigger

minipim is the source of truth. Your front end never writes back to it — it reads product data, renders it, and optionally caches it. When a merchandiser publishes a product change in minipim, a webhook fires and your storefront invalidates or rebuilds the affected pages.

Authentication

All API requests require a Bearer token. Create one under Settings → API keys in your minipim instance. On Hosted, tokens are scoped (read-only vs. read-write); use a read-only token for your storefront.

Authorization: Bearer YOUR_API_TOKEN

Fetching products

List products

GET /api/v1/products?family=apparel&channel=storefront&locale=en&page=1&limit=50

Returns a paginated list of products with their attributes scoped to the given channel and locale. Attributes not scoped to storefront are omitted automatically.

Response:

{
  "data": [
    {
      "id": "prod_01abc",
      "sku": "CDR-CRW-018",
      "family": "apparel",
      "attributes": {
        "title": "Cedar Crew Sweater",
        "material": "100% Merino Wool",
        "price": { "USD": "148.00", "CAD": "199.00" },
        "gallery": ["https://cdn.minipim.com/img/..."]
      },
      "variants": [...],
      "updatedAt": "2026-06-11T09:00:00Z"
    }
  ],
  "meta": { "total": 284, "page": 1, "limit": 50 }
}

Fetch a single product

GET /api/v1/products/CDR-CRW-018?channel=storefront&locale=en

You can look up by SKU, internal ID, or slug. The channel and locale params scope the response — only attributes available on that channel in that locale are returned.

Filter by category or collection

GET /api/v1/products?category=outerwear&channel=storefront&locale=en
GET /api/v1/products?collection=new-arrivals&channel=storefront&locale=en

Fetch variants

Variants are included in the product response by default. To fetch a specific variant:

GET /api/v1/products/CDR-CRW-018/variants/CDR-CRW-018-M-NAT

Webhooks

Webhooks let your infrastructure react to catalog changes without polling. Register an endpoint under Settings → Webhooks and select the events you want:

EventFires when
product.publishedA product is published to a channel
product.updatedAny attribute changes on a product
product.unpublishedA product is unpublished from a channel
variant.updatedA variant’s attributes or price changes
family.updatedAn attribute family definition changes

The payload includes the product ID, SKU, channel, and a timestamp. Your endpoint should respond with a 200 within 5 seconds — minipim retries up to 3 times on failure with exponential backoff.

Example payload:

{
  "event": "product.published",
  "channel": "storefront",
  "data": {
    "id": "prod_01abc",
    "sku": "CDR-CRW-018",
    "updatedAt": "2026-06-11T09:00:00Z"
  }
}

Keeping the front end in sync

Next.js with ISR

Fetch products at build time with getStaticProps and revalidate on demand via webhook:

// pages/products/[slug].tsx
export async function getStaticProps({ params }) {
  const product = await fetch(
    `${process.env.MINIPIM_URL}/api/v1/products/${params.slug}?channel=storefront&locale=en`,
    { headers: { Authorization: `Bearer ${process.env.MINIPIM_TOKEN}` } }
  ).then(r => r.json());

  return { props: { product }, revalidate: 3600 };
}

In your webhook handler, call res.revalidate('/products/' + sku) to flush the specific page immediately when a product publishes.

Astro with on-demand rendering

// src/pages/products/[slug].astro
const { slug } = Astro.params;
const res = await fetch(
  `${import.meta.env.MINIPIM_URL}/api/v1/products/${slug}?channel=storefront&locale=en`,
  { headers: { Authorization: `Bearer ${import.meta.env.MINIPIM_TOKEN}` } }
);
const { data: product } = await res.json();

Static build with full catalog fetch

For a fully static site, fetch all products at build time:

const res = await fetch(
  `${process.env.MINIPIM_URL}/api/v1/products?channel=storefront&locale=en&limit=1000`,
  { headers: { Authorization: `Bearer ${process.env.MINIPIM_TOKEN}` } }
);
const { data: products } = await res.json();

Then trigger a rebuild via your CI/CD pipeline from your webhook handler. This is simple and works well for catalogs under a few thousand products.

Multi-locale and multi-market

Pass locale and market params to get the right copy and pricing:

GET /api/v1/products/CDR-CRW-018?channel=storefront&locale=fr&market=CA

The response will include the French copy for text/richtext attributes and CAD pricing for money attributes. Attributes not localized fall back to the default locale automatically.

GraphQL API

minipim also exposes a GraphQL endpoint at /api/graphql for frontends that prefer it:

query ProductPage($sku: String!, $locale: String!) {
  product(sku: $sku) {
    id
    attributes(locale: $locale, channel: "storefront") {
      title
      material
      price(market: "USD")
      gallery { url alt }
    }
    variants {
      sku
      attributes(locale: $locale) {
        size
        color
        price(market: "USD")
      }
    }
  }
}

The GraphQL schema is generated from your attribute families, so it’s always in sync with your actual catalog structure.

Readiness and the storefront channel

Before a product appears in the API, it needs to be published to the storefront channel. The readiness rules for your storefront channel control the minimum required attributes. Until a product is published, it won’t appear in list endpoints — protecting you from half-complete products reaching your front end.

Caching recommendations

  • Cache product list responses at the CDN edge with a short TTL (60–300 seconds) and invalidate via webhook on product.published
  • Cache individual product pages more aggressively (up to 24 hours) and invalidate on demand
  • Never cache the /api/v1/products response in your application code without a cache-busting strategy — a stale product list is worse than a slow one

Known rough edges

List response shapes are inconsistent across endpoints. Most list endpoints return { data: [...], meta: { total, page, limit } }, but some return a bare array. Write your API client to handle both until this is standardized — something like:

const body = await res.json();
const items = Array.isArray(body) ? body : body.data;

This is most likely to bite you on /v1/attributes and a few other metadata endpoints. Product and variant list endpoints use the paginated envelope consistently.

Bulk attribute operations set one value across all products. The current bulk-set-attribute endpoint takes a single value and applies it to every product in the batch — useful for shared values (brand, season) but not for per-product values. For operations where each product needs its own value (like normalizing keyword arrays), you’ll need individual PATCH calls per product. A per-product bulk format ([{ productId, value }]) is on the roadmap.