Skip to main content
Every product change has a status that says whether it’s waiting for review or already resolved. A change starts pending and is resolved exactly once: confirmed, declined, or canceled. Whether a seller’s edit waits for an operator at all depends on the product_request feature flag and on the auto_confirm input.

The status lifecycle

The status field takes one of the four ProductChangeStatus values, exported from @mercurjs/types. Each resolution has its own workflow, which checks that the change is still pending, stamps the actor field, and emits an event. The ProductChangeStatus table: a change enters pending through stageProductChangeWorkflow, then confirmProductChangeWorkflow, rejectProductChangeWorkflow, or cancelProductChangeWorkflow moves it to confirmed, declined, or canceled, each emitting its own event and stamping its own actor field. The ProductChangeStatus table: a change enters pending through stageProductChangeWorkflow, then confirmProductChangeWorkflow, rejectProductChangeWorkflow, or cancelProductChangeWorkflow moves it to confirmed, declined, or canceled, each emitting its own event and stamping its own actor field. Confirming, declining, or canceling a change that isn’t pending fails with a NOT_ALLOWED error. Each resolution step saves the previous values, so if a later step fails, the workflow restores the change to pending.
The model also has requires_action_by, requires_action_at, and requires_action_reason columns, but ProductChangeStatus has no requires_action value and no workflow writes those columns.

Revision requests

A revision request sends a proposed product back to its seller without approving or rejecting it. requestProductChangeWorkflow checks that the product status is proposed, records an audit change with a CHANGE_REQUESTED action, and emits product.change-requested. The product stays proposed for the seller to revise. A revision request isn’t a fifth status, and it doesn’t resolve any pending change. It’s an already-confirmed entry in the product’s history. The operator’s message is stored twice: in the action’s details.message and in the change’s external_note.

Auto-confirm

Staging always runs autoConfirmProductChangeWorkflow right after the change is created. It runs confirmProductChangeWorkflow in the same workflow run when either condition holds:
  • The flag is off: the product_request feature flag is disabled
  • Forced: the caller passed auto_confirm: true, which reaches the workflow as force
Otherwise the change stays pending. The flag is on by default. Turn it off with an environment variable:
.env
With the flag off, every seller edit applies immediately and still leaves a confirmed change in the trail. The same flag also controls product creation: while it’s on, a seller can only create products as draft or proposed.

When Mercur forces auto-confirm

There’s no risk scoring: Mercur doesn’t classify edits as low or high risk. It forces auto-confirm in two places:
  • Draft deletes: productEditDeleteProductWorkflow defaults auto_confirm to true when the product is a draft
  • Operator deletes: the admin product delete route always passes auto_confirm: true
An auto-confirmed change goes through the same confirmProductChangeWorkflow as a manual approval. It emits product-change.confirmed and fires the same hooks, with confirmed_by set to the change’s author.