Collections & pages
Two related surfaces: collections group products for merchandising;
pages are standalone CMS content (about, policies, landing copy). Both
follow the same status + archive conventions as the catalog. Examples reuse
the api() helper.
Collections
Read (public)
const collections = await api<Collection[]>("/public/collections"); // ACTIVE only
const collection = await api<Collection>("/public/collections/new-arrivals");
const itemsInIt = await api<Item[]>("/public/collections/new-arrivals/items");A collection that isn't ACTIVE reads as "no items" on the public surface —
archived items are hidden from the listing too.
Collection IDs are opaque, UUID-shaped references from the current catalog in
the matching tenant/environment. Ordinary edits and archive/restore retain
them; an explicit identity migration can replace them. Use each current item's
collectionIds for cart-preview membership, rather than a saved collection ID
or a name-based guess. See catalog identity and retirement
and saved-basket recovery.
Create & curate (merchant)
POST /merchant/collections — permission: catalog:write. Owner/Admin retain
broad access; Staff needs that group.
const collection = await api<Collection>("/merchant/collections", {
method: "POST",
token,
body: JSON.stringify({
name: "New Arrivals", // required
slug: "new-arrivals", // required, unique per tenant
status: "ACTIVE", // required: ACTIVE | DRAFT
description: "This season's fresh stock.",
}),
});
// Add items (already-present items are skipped, new ones appended):
// firstItem and secondItem are current item records from this tenant's catalog.
await api(`/merchant/collections/${collection.id}/items`, {
method: "POST", token, body: JSON.stringify({ itemIds: [firstItem.id, secondItem.id] }),
});
// Remove one (idempotent → { removed: 0 | 1 }):
await api(`/merchant/collections/${collection.id}/items/${firstItem.id}`, {
method: "DELETE", token,
});
// Reorder items within the collection (send the full current set):
await api(`/merchant/collections/${collection.id}/items/order`, {
method: "PATCH", token, body: JSON.stringify({ itemIds: [secondItem.id] }),
});archive / restore and a collection-level order endpoint mirror the
catalog — same shapes, same "send the complete set" rule for reordering.
CMS pages
Pages use PUBLISHED | DRAFT (not ACTIVE). The public surface serves only
PUBLISHED; DRAFT and archived pages read as 404.
Read (public)
const pages = await api<Page[]>("/public/pages"); // PUBLISHED only
const page = await api<Page>("/public/pages/shipping-policy");Create (merchant)
POST /merchant/pages — permission: content:write. Owner/Admin retain broad
access; Staff needs that group.
await api<Page>("/merchant/pages", {
method: "POST",
token,
body: JSON.stringify({
title: "Shipping policy", // required
slug: "shipping-policy", // required, unique per tenant
content: "<h1>Shipping</h1>…", // required
status: "DRAFT", // required: PUBLISHED | DRAFT
excerpt: "How and when we ship.", // optional
}),
});PATCH /merchant/pages/:id updates; archive soft-deletes. Restoring a page
returns it as DRAFT (conservative — you re-publish explicitly) rather than
silently re-publishing.
Related
- Catalog: products & variants — the items collections group
- Media uploads — collection cover images