Skip to content

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.

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):
await api(`/merchant/collections/${collection.id}/items`, {
  method: "POST", token, body: JSON.stringify({ itemIds: ["id-a", "id-b"] }),
});
 
// Remove one (idempotent → { removed: 0 | 1 }):
await api(`/merchant/collections/${collection.id}/items/id-a`, {
  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: ["id-b", "id-c"] }),
});

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.