Target pills
A product can be published to more than one target and environment, for example a QA VTEX store and a production VTEX store, or a VTEX store and a Shopify store. MerchantOps shows one pill per enabled target, so you see each destination on its own: one product can be live on production and modified on QA at the same time. The same pill appears everywhere publish status is shown: the product list, product detail, the catalog and pricing batch pages, and price detail. Each pill is in one of these states:
If more than one applies, the state higher in the table wins. A target that is
live, modified, or failed and also has a publish scheduled keeps its own
state, and the hover shows the next scheduled publish.
What the hover shows
Hover a pill to see the facts behind it:- the target name and environment, with a one-line description of the state;
- the version published (for a price: the SKU, price type, amount, and effective date);
- when it was published;
- who published it. A publish that ran automatically on its effective date reads scheduled · approved by the member who approved the batch;
- the batch that published it and the job that ran it, each linking to its page;
- the next scheduled publish, or, on an older product version, which version is live on that target now.
Publishes made before MerchantOps recorded the batch, job, and member on every
publish may not have all three. Their pills still show the correct state; the
hover just leaves out the details, or the links, it cannot resolve.
The summary status still exists
The product list used to show a summary pill (Draft, Published, or Modified) next to the per-target pills. It has been removed; the per-target pills say the same thing more precisely. The summary is still computed, though:- The product list’s All Publish Status filter still filters on Draft (never published anywhere), Published (in sync everywhere it is published), and Modified (edited since it was last pushed to at least one target).
- The API still returns it as
publish_status, with the valuesdraft,live, andchanged.
A batch approved without a publishing target is shown as active in
MerchantOps but is never published, so it produces no publish status. Its
products keep never published pills (and a Draft summary) until a batch
publishes them to a target.
Older Shopify publishes may read modified. Shopify publishes made before
MerchantOps began recording a content fingerprint for them lack what the
comparison needs, so a product published to Shopify then can show modified ● even though nothing
changed. Republishing the product to that Shopify store records the fingerprint
and turns the pill green.
Where you see the pills
Product list
Product list
The Status column shows one pill per enabled catalog target. The
column header’s legend explains each state.
Product detail
Product detail
- Header. On the latest version, the pills are exactly the ones in the product list, so the two never disagree. Enrichment adds versions often, so the latest version may be #12 while a target holds #10 with identical content. The #12 pill is then live, and its hover says the target holds #10 with identical content. On an older version, each target shows live if that version is the one live there now, superseded ✓ if it was published there and later replaced, failed ✕ if its latest attempt there failed, and otherwise scheduled or never published.
- Version cards. Each version card shows a compact pill (for example PROD or QA) for every target that version was published to or attempted on. See Versioning.
- Publishing history. A collapsible section listing every publish of the product, failures included, with version, target, status, time, who, batch, and job.
- A Prices ↗ link opens the product’s price detail page.
Catalog batch
Catalog batch
The records table’s Publish column shows a pill for each record against
the batch’s target. A record in an approved batch whose effective date is
still ahead shows scheduled. See
Publishing overview.
Pricing batch
Pricing batch
The Price Records table has a Publish column. SKU rows show one
pill for the batch’s target; style and color rows roll their SKUs up (for
example “VTEX QA · 1/2”), reading as a warning when only some published and
as failed when none did. See
Price batches.
Price detail
Price detail
An All variants · last publish per target row summarizes the product’s
prices with one pill per enabled pricing target, including hollow
never published pills. Variant and record rows show a pill for every
enabled target too. See
Price records.
From the API, MCP, or CLI
Agents and integrations can read the same facts.product:read
The product’s publish history plus its current per-target status.
historylists one row per batch, target, and environment, newest first. A version published to the same target by two batches has two rows; republishing the same batch replaces that batch’s row. Each row carriesversion, the target,status(successorfailed),attempted_at,attribution(batch, job, who, and how it was triggered), anderroron failures.published_atis the attempt time, failed rows included.versionisnullfor an older record whose version can’t be determined.- Failed attempts are left out unless you pass
include_failed=true. The default returns successes only, as before. historyis capped at 200 rows;truncatedistruewhen more exist.currentholds the summarypublish_statusand one chip per enabled target.livemaps each target to the version live there now,scheduledlists upcoming publishes, andtargetslists every enabled catalog target, including ones never published to.
product:read
The product list and search endpoints and
GET /api/products/{key} return
one chip per enabled catalog target in publish_chips. The single-product
GET /api/products/{key} used to return an empty list here; it now matches
the list. Each chip keeps its state of in_sync, drifted, or
never_published, and adds attribution, last_attempt (the newest
attempt, including failures), next_scheduled, and in_flight (a publish
running now). The pill states above are derived from these fields.pricing:read
The pricing equivalent.
targets has one entry per enabled pricing target,
with state (published, failed, or never), the last success, the last
attempt, next_scheduled, and in_flight. scheduled lists approved price
batches that will publish the product’s prices; history lists one row per
record, batch, and target, newest first, failed and skipped attempts included; skus rolls the
same status up per SKU. history_limit (default 200, maximum 1000) caps the
history, and truncated says whether it was cut.get_product_publish_status and
get_price_publish_status answer “when, and to where, was this last
published”, with who, batch, job, scheduled publishes, and history; both flag
a partial history explicitly (truncated, size_limited, and a
history_note) rather than letting an agent treat a cut list as complete.
From the CLI, use mo api products get-publish-history and
mo api pricing get-price-publish-status-by-product.
Per-target, per-environment outcomes
Under the hood, every record in a batch carries an outcome for each target and each environment it was published to. Because outcomes are keyed by both the target and its environment, a single record holds an independent QA result and production result without one overwriting the other. A recorded outcome captures what happened on that publish:
A
cancelled record was never sent, so it carries no response code, no
attempts, and no error.
What each outcome means
Succeeded
Succeeded
The record was accepted by the destination. For a catalog record that means
the product/SKU was created or updated; for a price record, the price was
set. The record shows
success and drops out of any pending-publish queue.Failed
Failed
The destination rejected the record or a permanent error occurred — for
example a variant with no matching SKU in the store. The outcome carries an
error summary and a response code. Transient problems (rate limits,
temporary server errors) are retried automatically first; only a record that
can’t be published after retries lands as failed.Skipped
Skipped
The record was intentionally not published — for instance a connector you
built yourself decided the record does not apply
to its system. Nothing went wrong, and nothing was published.A skip is terminal: it counts toward the batch’s completion, so a batch
containing skipped records still reaches a final state. It is tracked in its
own skipped count, never folded into published or failed. Because a skip
is not a publish, it weighs against the batch’s success ratio the same way a
failure does.
Cancelled
Cancelled
The publish was cancelled before this record was sent. Nothing was
published for it and nothing went wrong with the record itself — it can be
published again by re-driving the batch.A cancellation is the one outcome that does not move the batch’s
published, failed, or skipped counts. It is counted on the
job instead, and the
cancellation itself moves the batch to its final state — the counts are
never expected to add up on a cancelled run.
Batch-level rollup
As records report outcomes, the batch tracks published, failed, and skipped counts. Every record that publishes, fails, or is skipped is accounted for, and once they add up to the batch’s total the batch reaches a final state:- Published — at least 95% of the batch’s records succeeded.
- Partial — fewer than 95% succeeded. Review the failed records’ error summaries, fix the underlying data, and publish again.
“Published” means the publish actually finished — never just started. A
batch’s Published timestamp and publisher are stamped only once records have
genuinely reported outcomes, not the moment a publish attempt began. If a
catalog batch is sitting in a publishing state, its Published timestamp
stays blank until that run completes; it no longer shows a timestamp (and
“published by”) for a run that hasn’t reported a single outcome yet.
The batch badge and the job status are two different readings, and they can
disagree. The 95% threshold above decides the batch badge only. The job that
did the publishing settles on its own
counters: a
catalog publish that got most of its records through reports Completed with
Errors on the Jobs page while the batch reads Partial, and only a run that
published nothing at all reports Failed. A mostly-successful publish used to
report Failed here, because the batch’s Partial verdict was written onto the
job; it no longer does. Size the re-drive from the job’s failed count, and use
the per-record outcomes to see which records to fix.
A cancelled publish rolls up differently. Cancelled records move none of
these counts, so a cancelled batch never reaches its total. Cancelling finalizes
the batch directly instead: Partial if at least one record published,
Failed if none did. Either way the batch is re-drivable — see
Cancelling a catalog publish.
Batches sent to a self-built (poll) target roll up the same way. The outcome
POST that completes the batch gets the final state back in its
batch_final_status field — see
build your own connector.Some connectors surface warnings alongside outcomes — non-fatal issues that
didn’t fail the publish but need attention, such as a brand or category that
doesn’t exist in the destination yet (see VTEX). A warning
doesn’t turn a
success into a failed; it flags follow-up work.Fixing a partial batch
Partial (or Failed) means some records didn’t make it. Fix the underlying data — the product, variant, or price record the errors point at — and then re-drive the batch:
Republishing a catalog batch and pricing Publish now / Republish all
require approval permission (
catalog_batch:approve and pricing:approve
respectively).
A catalog publish that keeps stalling stops retrying itself automatically.
MerchantOps also detects a catalog publish whose records go quiet — nothing
succeeds, fails, or is skipped — and finalizes it on its own so it isn’t left
sitting in a publishing state forever. Normally that also puts it back in
line to be re-attempted. If the same batch stalls this way repeatedly with
still no records reporting an outcome, MerchantOps stops re-attempting it
automatically after a few tries (three by default) rather than retrying
indefinitely with nothing to show for it. From that point the batch needs a
manual Republish once the underlying issue (for example, a publishing
target that isn’t reachable) is resolved — republishing manually also resets
the stall count.
Resetting a stuck pricing publish
A publish that never reports every record leaves the batch sitting in its publishing state, which also blocks Publish now (the batch looks like it is still in flight). Resetting it force-finalizes the batch so it can be published again:pricing:approve, and it never claims success: the batch lands
Partial if at least one record published and Failed if none did. Both
are re-drivable with Publish now.
The reset is refused with a 409 when there is nothing to unstick — the batch is
not publishing — or when the publish started too recently (roughly the last ten
minutes), so a run that is genuinely still in flight is never interrupted. A poll
connector can legitimately take minutes between fetching a record and reporting
it; give it that time before resetting. The reset is recorded in the batch’s
activity history.
Next steps
Publishing overview
How batches, targets, and scheduling fit together.
Build your own connector
Report outcomes from your own target and see them here.