Check Store Products Are Archiveable
POST/api/storefront/instances/:id/products/archiveable
Pre-flight check: which of these store products can be archived, and why not.
This endpoint currently requires session authentication; Personal Access Token scope support is in progress.
Returns one row per requested product — id, archiveable (boolean) and reason. reason is false when the product can be archived, and a string naming the blockers when it cannot. The only blocker today is that the product appears on imported store orders, reported as Sales Channel Product has Sales Channel Orders.
Worth calling first: PUT /archive SILENTLY SKIPS products that have store orders and still reports success, so without this check you cannot tell which ones were left behind.
Note the verb: this endpoint is a POST.
Selecting what to act on — the shared selection contract used by this endpoint, POST /bulk-map, POST /create-sku-products, PUT /archive, PUT /unarchive, POST /archiveable, PUT /delete and PUT /deleteable:
{"ids": [16, 17]}— an explicit list of store-product ids.{"apply_to_all": true}— every product on the instance that the product list would show with no filters, i.e. every NON-ARCHIVED product.{"apply_to_all": true, "filters": { ... }}— every product matching those filters, however many pages that spans. This is "select everything matching what I am looking at".
filters carries the SAME keys the product list accepts, but written as bare column names with no filter[...] wrapper — e.g. {"mapping_status": "unmapped", "status": "active", "search": "door", "search_columns": "sku,unified_title", "price.greater_than": 100, "filter_groups": "<base64 tree>"}. A nested {"filter": {...}} object, literal filter[col] keys, and a JSON string of the whole object are all accepted too. Display and paging keys (page, per_page, sort, included, total, table_specifications, query) are ignored — the selection is never limited to one page. search is deliberately NOT ignored.
Two rules worth knowing:
- If
filtersdoes not mentionarchived, archived products are excluded, exactly as the list hides them by default. A sweep can therefore never reach a product the list was not showing. - A
filterspayload that cannot be parsed, or afilter_groupstree that cannot be parsed, is rejected with 422 rather than being treated as "no filters" — which would have acted on every row.
Body fields: ids (array of integers — required unless you send a select-all flag, and every id must belong to this connection or the call is rejected with 422), apply_to_all (boolean), mode (send "all" as an equivalent of apply_to_all: true with no filters), filters (object or JSON string).
Authentication: Bearer token (session or PAT).
Request
Responses
- 200
- 401
- 403
- 404
- 422
- 429
OK
Response Headers
Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the Errors guide.
Forbidden — the token lacks a required scope, the endpoint is not available to API tokens, or the user behind the token lacks the permission. A human must adjust the token scopes or user permissions; do not retry.
Not found — no record with the given identifier (or the route does not exist). Verify the ID before retrying.
Validation failed — the body is a field → messages map (Laravel shape) or the platform envelope with a stable machine-readable code. Fix the payload and resubmit.
Rate limited — platform limit is 1,000 requests/min; individual tokens may carry lower limits. Honor the Retry-After header before retrying. See the Rate Limits guide.