Bulk Delete SKU Orders
POST/api/storefront/instances/:id/orders/bulk-delete-sku-orders
Delete the SKU.io sales orders that were created from these store orders, while keeping the mirrored store orders themselves. Use it to undo a bad import without losing the mirror.
This endpoint currently requires session authentication; Personal Access Token scope support is in progress.
Both selection paths are narrowed to orders that actually have a linked SKU sales order — unlinked orders are never counted, so total_orders reflects real work. An order whose SKU sales order has returns already consumed against it is reported as a failure rather than deleted.
Request body
ids(array of integers, required unlessapply_to_allis sent).apply_to_all(boolean, optional).filters(object, nullable) — the set to target whenapply_to_allis true.archive_orders(boolean, optional, default false) — also archive each mirrored store order, which stops the next import run simply recreating the SKU sales order you just deleted.
Choosing the orders Send exactly one of the two selection shapes:
ids— an explicit list of mirrored order ids. Ids that belong to another connection are silently dropped.apply_to_all: trueplusfilters— the action targets every order the filters match, however many that is, not just a page of them. Thefiltersobject is resolved server-side against the same filter setGET /ordersaccepts, so what you saw in the list is exactly what is acted on.
Inside filters (every key optional, all bare column names — no filter[...] brackets needed, though filter[status] style keys and a nested filter object are both unwrapped for you):
search— the same free-text search as the list, honoured like any other filter.search_columns,search_strict_columns— narrow or tighten that search.status,payment_status,currency_code— comma-separated value lists.sku_order—with,without,abandoned,failedorheld.has_unmapped_items— boolean.archived—only/archived, orall. Leaving it out means active orders only, matching the list's own default, so a filter-less apply-to-all never sweeps archived orders.placed_from,placed_to— date bounds.ids— a comma-separated id list, as a filter rather than a selection.<column>.<operator>— any advanced operator key the list accepts (e.g.total.greater_than).filter_groups— a base64-encoded JSON condition tree.
Pagination and display keys (page, per_page, sort, included, total, query) are ignored if present.
Failing closed. A filters payload that cannot be parsed, a malformed filter_groups tree, or a legacy {conjunction, filterSet} payload returns 422 rather than being treated as "no filters" — silently matching every order would be the wrong guess for a bulk action.
Immediate or background. A single order is processed inline and the outcome is in the response (tracked_job_log_id is null). More than one order is queued as a background task and the response returns straight away with tracked_job_log_id — the handle you poll for progress, per-order results and failures. total_orders is how many orders were selected.
Requires the integrations.update permission.
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.