---
title: "Bulk Action Product"
method: POST
path: "/products/bulk_action"
---

# Bulk Action Product

`POST /products/bulk_action`

## Request

- Base URL: `https://dc-region.zuperpro.com/api`
- URL: `https://dc-region.zuperpro.com/api/products/bulk_action`
- Auth: API key in header `x-api-key`

Performs a bulk action on a set of products. The product set is determined either by an explicit `product_uid` array, or by `filter_rules` (v3) / legacy `filter.*` fields — `product_uid` is merged in additively ($in), not used as an exclusive selector. If `filter_rules` is a non-empty array it takes priority as the v3 filter engine; otherwise the legacy `filter` object is used.

All filter-driven actions (`update_field`, `update_availability`, `send_qr_codes`, `delete_products`) are capped by the account's `bulk_action_limit` policy (falls back to a platform default) and apply synchronously, except `send_qr_codes`, which streams a PDF directly in the response for under 100 matched products and emails it asynchronously for 100 or more. `link_supplier_provider` is a structurally distinct action: it does not use `product_uid`/`filter_rules`/`filter` matching or the bulk action limit at all — it links an explicit `vendor_catalog` array of entries to a `supplier_provider_uid`, and always runs asynchronously (queued to a worker), returning a `job_id` immediately.

`update_field` silently skips products that don't support the given field for their type/pricing-level (e.g. BUNDLE/LABOR products for `product_type`, or products without a `purchase_price` for `markup`) rather than rejecting the whole request — the response does not list which uids were skipped.

## Request body

- object
  - `action` 'update_field' | 'update_availability' | 'send_qr_codes' | 'delete_products' | 'link_supplier_provider': update_field: change one field across all matched products. update_availability: flip availability. send_qr_codes: generate/send QR-code labels. delete_products: soft-delete matched products. link_supplier_provider: link vendor catalog entries to a supplier provider (not filter-driven, see below).
  - `product_uid` string[]: Optional explicit list of product uids. Combined additively ($in) with filter_rules/filter. Not used by link_supplier_provider.
  - `filter_rules` object[]: v3 filter engine rules used to select the product set. Takes priority over `filter` when non-empty. Not used by link_supplier_provider.
  - `action_options` object
    - `custom_field` object: update_field only. Mutually exclusive with the plain field options below.
      - `label` string
      - `value` string
      - `type` string
      - `read_only` boolean
      - `hide_to_fe` boolean
      - `hide_field` boolean
      - `group_uid` string
      - `group_name` string
    - `brand` string: update_field only. Ignored for supplier-imported products.
    - `product_type` 'PRODUCT' | 'SERVICE' | 'PARTS': update_field only. Skipped for BUNDLE/LABOR products.
    - `specification` string: update_field only.
    - `product_category` string: update_field only. A category_uid. Ignored for supplier-imported products.
    - `business_unit` string: update_field only. Comma-separated bu_uid values.
    - `purchase_tax` number: update_field only. >= 0. Recomputes purchase_price/pre_tax_cost (and price/markup_price if a markup is active) per product from its own stored pre_tax_cost. Skipped for non-PRODUCT/PARTS types.
    - `markup` object: update_field only. Skipped for products without a purchase_price, and for BUNDLE+ROLLUP products.
      - `markup_type` 'FLAT' | 'PERCENTAGE' | 'MULTIPLIER'
      - `markup_value` number
    - `formula` string: update_field only. A formula_uid. Skipped for BUNDLE+ROLLUP products.
    - `is_available` boolean: update_availability only. The request is automatically scoped to products currently in the opposite state.
    - `supplier_provider_uid` string: link_supplier_provider only. Required.
    - `vendor_catalog` object[]: link_supplier_provider only. Required, non-empty.
      - `zuper_product_uid` string, required
      - `zuper_product_option_uid` string
      - `supplier_product_uid` string, required
      - `supplier_product_option_uid` string, required
      - `vendor_sku` string, required
  - `filter` object: Legacy filter object, used only when filter_rules is omitted or empty.

## Response `200`

200

- object
  - `type` string
  - `title` string
  - `message` string
  - `data` unknown

## Other responses

- `400`: 400

## Changes

- **2026-11-03** `3c21bc232e16`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/zuper/apis/zuper-pro-api/changes/products/bulk_action/post.md)

---

[API](https://skmtc.dev/zuper/apis/zuper-pro-api.md) - [All operations](https://skmtc.dev/zuper/apis/zuper-pro-api/llms.txt) - [OpenAPI document](https://skmtc.dev/zuper/apis/zuper-pro-api/revisions/3c21bc232e16?raw)
