> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mercurjs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Product Edit module

> Data models and lifecycle for product change requests on master products.

The Product Edit module owns the **product change** — the request/approval record through which sellers propose edits to the shared catalog. Products in Mercur are master records that no seller owns; any seller may request a change to any product, and the operator confirms, declines, or lets the platform auto-confirm it.

## The master products model

A `product` row is a shared catalog entry — it has no owner column. Three rules follow:

* **Selling eligibility** is a separate allowlist: the `product_seller` link. One or more rows restrict the product to those sellers; zero rows mean the product is unrestricted.
* **Creator attribution** lives on the change record (`product_change.created_by` plus a `PRODUCT_ADD` action), and is used only for scoping vendor list views — a seller sees products they created, plus published products not restricted to other sellers.
* **Edits are requests.** Vendor product update endpoints stage a change instead of writing directly; there is no ownership gate on requesting a change.

## Data models

### `ProductChange`

Table `product_change`, ID prefix `prodch`.

| Field                                                                  | Type                   | Notes                                    |
| ---------------------------------------------------------------------- | ---------------------- | ---------------------------------------- |
| `product_id`                                                           | text                   | Target product                           |
| `status`                                                               | enum                   | `ProductChangeStatus`, default `pending` |
| `created_by`                                                           | text                   | Nullable; requesting actor               |
| `internal_note` / `external_note`                                      | text                   | Nullable                                 |
| `confirmed_by` / `confirmed_at`                                        | text / dateTime        | Nullable; audit trail                    |
| `declined_by` / `declined_at` / `declined_reason`                      | text / dateTime / text | Nullable                                 |
| `canceled_by` / `canceled_at`                                          | text / dateTime        | Nullable                                 |
| `requires_action_by` / `requires_action_at` / `requires_action_reason` | text / dateTime / text | Nullable; "changes requested" state      |
| `metadata`                                                             | json                   | Nullable                                 |

Relations: `actions` (one-to-many, deleted with the change).

### `ProductChangeAction`

Table `product_change_action`, ID prefix `prodchact`. The ordered operations a change applies:

| Field           | Type          | Notes                                                                      |
| --------------- | ------------- | -------------------------------------------------------------------------- |
| `product_id`    | text          |                                                                            |
| `ordering`      | autoincrement | Apply order                                                                |
| `action`        | text          | Operation type (e.g. `PRODUCT_ADD`, update, variant, attribute operations) |
| `details`       | json          | Default `{}`; the operation payload                                        |
| `applied`       | boolean       | Default `false`                                                            |
| `internal_note` | text          | Nullable                                                                   |

## Enums

```ts theme={null}
enum ProductChangeStatus {
  PENDING = "pending",
  CONFIRMED = "confirmed",
  DECLINED = "declined",
  CANCELED = "canceled",
}
```

## Links

| Link                  | Purpose                                       |
| --------------------- | --------------------------------------------- |
| `changes` (read-only) | Product → its change records via `product_id` |

## Service

`ProductChangeModuleService` extends `MedusaService` with auto-generated CRUD only — the lifecycle logic lives in workflows: `createProductChangeWorkflow`, `stageProductChangeWorkflow`, `confirmProductChangeWorkflow`, `autoConfirmProductChangeWorkflow`, `cancelProductChangeWorkflow`, `rejectProductChangeWorkflow`, and the `applyProductChangeActionsWorkflow` family that replays the staged actions onto the product. See [Workflows](/rc/references/workflows#product-edit).

## Related endpoints

* Vendor: `POST /vendor/products/:id` and variant/attribute sub-routes stage changes; `GET /vendor/products/:id/preview` shows the product with pending changes applied; `POST /vendor/products/:id/cancel` withdraws a request.
* Admin: `GET /admin/products/:id/preview`, `POST /admin/products/:id/confirm`, `POST /admin/products/:id/reject`, `POST /admin/products/:id/request-changes`, plus `POST /admin/product-changes/:id/confirm` and `/cancel`.

## Next steps

<CardGroup cols={2}>
  <Card title="Product Attribute module" href="/rc/references/modules/product-attribute" />

  <Card title="Offer module" href="/rc/references/modules/offer" />
</CardGroup>
