Headless means minipim doesn’t render anything. It holds the catalog — products, variants, categories, brands, media and content pages — and your storefront reads it over an API, whatever it’s built with. When something changes, a webhook tells your site so it can revalidate just the pages that moved.
The admin UI is built on exactly the same API, so there’s nothing the admin can read that your storefront can’t.
The API
Every endpoint lives under https://api.minipim.com/v1/ (or your own instance’s URL, if you self-host). The full, generated reference — every route, parameter and response shape — is at api.minipim.com/docs.
Authentication. Create a key under System → API keys in the minipim admin and send it as a bearer token:
GET /v1/products?status=active&limit=50
Authorization: Bearer pim_your_key
The key carries its organization, so there’s no tenant header to send. Keep it server-side — it isn’t a public key.
What a storefront typically reads:
| Endpoint | Use |
|---|---|
GET /v1/products | List and filter: status, q, slug, categoryId, brandId, tag, updatedSince, numeric attribute ranges, sorting |
GET /v1/products/{id} | One product; add ?locale= and/or ?channel= for resolved attribute values |
GET /v1/products/{id}/variants | A product’s variants (SKUs live here) |
GET /v1/products/{id}/relationships | Related products |
GET /v1/categories | The category tree (flat, with parentId) |
GET /v1/brands | Brands, by slug or search |
GET /v1/content/by-slug/{slug} | A content page or blog post |
Lists return { data, limit, offset, hasMore } and page with limit (up to 200) and offset. Money is always integer cents plus a currency — { "amount_cents": 14800, "currency": "USD" } — and measurements carry their unit.
Routing by slug. Product detail is looked up by id, so resolve a URL slug through the list filter first: GET /v1/products?slug=merino-crew&status=active&limit=1.
Pass status=active. List endpoints return drafts and archived items too unless you filter, and so does the content-page slug lookup.
Localized values. Attributes can vary by locale and channel. Ask for GET /v1/products/{id}?locale=fr&channel=web and the response adds resolvedAttributes, each value picked by the closest match: exact locale and channel, then locale only, then channel only, then the unscoped value.
The TypeScript SDK
@minipim/sdk is a typed client generated from the same OpenAPI spec, with pagination helpers, money and attribute helpers, and webhook verification:
import { createMinipimClient } from '@minipim/sdk';
const pim = createMinipimClient({
baseUrl: 'https://api.minipim.com',
apiKey: process.env.MINIPIM_API_KEY!,
});
const { data } = await pim.GET('/v1/products', {
params: { query: { slug: 'merino-crew', status: 'active', limit: 1 } },
});
Webhooks
To receive webhooks, install the Headless connector under Connectors and give it:
- a destination URL — a public
httpsendpoint on your site; - a secret of at least 16 characters, used to sign every delivery;
- the events you want — exact names like
product.updated, or patterns likeproduct.*.
By default a headless connector subscribes to changes on products, variants, categories, brands, content pages, media, modifiers and related products, plus completed connector syncs.
Each delivery is a POST with an X-MiniPim-Event header, an X-MiniPim-Signature-256 header (sha256= + an HMAC-SHA256 of the raw body), and a body like:
{
"name": "product.updated",
"organization_id": "…",
"entity_id": "…",
"actor": { "type": "user", "id": "…" },
"timestamp": "2026-10-07T16:02:11.000Z",
"payload": { "changed_fields": ["attributes"] }
}
Payloads are deliberately thin: they say what changed, and your handler re-fetches the entity by entity_id. Verify the signature before trusting anything:
import { verifyMinipimWebhook } from '@minipim/sdk/webhook';
export async function POST(req: Request) {
const rawBody = await req.text();
const ok = verifyMinipimWebhook({
rawBody,
signature: req.headers.get('x-minipim-signature-256') ?? '',
secret: process.env.MINIPIM_WEBHOOK_SECRET!,
});
if (!ok) return new Response('bad signature', { status: 401 });
// revalidate the pages for JSON.parse(rawBody).entity_id
return new Response('ok');
}
On edge runtimes, use verifyMinipimWebhookEdge from @minipim/sdk/webhook-edge instead. The connector page in the admin shows recent deliveries and can send a test event.
Limits to design for
- Webhooks aren’t retried. A delivery your endpoint misses is not resent. Pair webhooks with a periodic catch-up using
GET /v1/products?updatedSince=…and revalidate anything newer than your last sync. - Lists return stored values.
localeandchannelresolution happens on the product detail endpoint, not on lists. - Variants are a separate call from the product.
- No inventory. Stock isn’t part of minipim; keep it in your commerce platform.
Using an AI agent to maintain the catalog behind your storefront? The MCP server writes through this same API, so its edits trigger the same webhooks.