Catalogue & product data
Product Archival
Generated from docs/plan/core/product-archival/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Core feature for archiving old products (via status=‘archived’) without deleting them. Merchants can hide products from the storefront while preserving historical data for orders and analytics. Combined with product-status, this replaces hard deletion with soft archival.
The problem
Merchants want to hide old products without deleting; they need to preserve historical data.
What it does
- Archive action: admin can set status=‘archived’ (via product edit or bulk edit) to hide product from storefront
- Visibility: archived products do not appear in GET /products (storefront default) or GET /products?status=published
- Historical preservation: order line items still reference archived products (by variant/SKU/name snapshot); no data loss
- Search inclusion (optional): archived products excluded from storefront search/filtering
- Admin filter: GET /products?status=archived (admin only) shows only archived products
- Restore action: admin can change status back to ‘draft’ or ‘published’ to re-activate
- Archive reason (optional): merchant can log reason for archival (e.g., ‘Discontinued’, ‘Out of stock’, ‘Seasonal’)
- Archive date: track when product was archived (implicit in deleted_at or explicit archive_reason_date)
- Bulk archive: bulk edit to archive many products at once
- Archive report: admin can export list of archived products (date archived, reason)
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Hard deletion (permanent removal from database) — only soft archival via status or deleted_at
- Automatic archival based on inactivity (no sales in 90 days) — merchant archives manually or via plugin
- Archival workflows (e.g., admin approval before archiving) — no workflow; admin can archive freely
- Archived product re-activation with price/availability updates — restore is simple; merchant updates details after
Data model
Uses existing Product.status field and deleted_at (nullable). Optional: add product.archive_reason (string, e.g., ‘Discontinued’, ‘Seasonal’, ‘Low sales’), archive_reason_date (timestamp).
API
- PUT /products/:id — set status=‘archived’ (soft delete); preserves data
- GET /products?status=archived — retrieve archived products (admin only)
- GET /products?status=archived,published — retrieve published and archived (admin only, no storefront)
- PUT /products/:id — restore: change status from ‘archived’ to ‘draft’ or ‘published’
- GET /products/:id — retrieve any product (storefront will not show archived in listing, but admin can query by ID)
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Archive implemented via product-status (status=‘archived’). Bulk edit includes archive action. Archive report: filter products by status=‘archived’, export as CSV with date archived and any reason field. Optional: archive detail page showing when each product was archived and why.
The seam — why this is core
Core owns soft archival via status field. Hard deletion is not provided; merchants use status=‘archived’ as a permanent hide. This preserves data for audit and analytics.
Core owns the interface + honest archive tool; product lifecycle is infrastructure, not a support commitment or credential.
Dependencies
- product-status (core; status field drives archival)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Setting status=‘archived’ hides product from storefront (GET /products does not return it).
- Archived product can be queried by ID in admin mode (GET /products/:id returns it, but storefront won’t link to it).
- GET /products?status=archived (admin auth) returns only archived products.
- Historical order line items still show product name/sku/price even if product is archived (snapshot, not live reference).
- Changing status from ‘archived’ to ‘published’ re-activates product on storefront.
- Bulk archive (PATCH /products/bulk with {status: ‘archived’}) archives 100 products at once.
- Exporting archived products as CSV includes name, sku, archived_at, archive_reason columns.
Risks
If storefront or search queries do not exclude archived products, they appear to customers. If historical order references to archived products are broken, order details become inaccurate. If merchants confuse archival with deletion, they expect data to be gone when it’s actually preserved.
Commercial context
| Suggested price | free (core) |
| Rival anchor | Shopify: included (Hidden products); Magento: included |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.