Skip to main content
MerchantOps ships with built-in connectors (see VTEX), but you can also build your own. This is the right approach when the destination is an internal system — an ERP, a proprietary pricing platform, a custom storefront extension — that a built-in target can’t reach. A custom connector is a small service you run. It never connects to MerchantOps internals. Instead it talks to a pull-based HTTP API: MerchantOps holds the approved, versioned records; your connector polls for the ones fanned out to your target, pushes them into your system, and reports each outcome back. Everything is scoped to your organization by an API key, so a misconfigured connector can never touch another tenant’s data.

The loop

Every connector, regardless of destination, runs the same loop:
1

Poll for pending records

Ask MerchantOps for records that have been approved and routed to your target but not yet reported as done.
2

Transform

Map each MerchantOps record onto whatever shape your system expects (field names, units, identifiers).
3

Push to your system

Write the record into your destination — call its API, update your database, drop a file, whatever your system needs.
4

Report the outcome

Tell MerchantOps whether each record succeeded or failed. Reported records drop out of the next poll; unreported ones come back so you can retry. Send each outcome exactly once — see Retries and idempotency below.
Today the pull-based connector API covers pricing publishing — the loop applies to price records routed to your target. For catalog publishing, use a built-in connector such as VTEX.

Setup

1

Register the target

In Settings › Publish Targets, add an integration for your custom target with a target key (for example custom) and a poll delivery mode. No credentials for your system are stored on the MerchantOps side — your connector authenticates to your system itself.
2

Create an API key with a role that grants the connector permissions

The connector API is gated on two permissions:Settings › API Keys has no scope picker. You create a key and assign it one or more roles, and the key inherits whatever permissions those roles bundle. Roles themselves are defined for your organization by MerchantOps, not in the app.The standard Owner / Admin / Editor / Viewer roles don’t include the connector permissions — so unless your organization already has a purpose-built connector role, ask your MerchantOps contact to set one up bundling the two permissions above, then assign it when you create the key.
The poll endpoint accepts either permission; reporting an outcome requires pricing.publish.outcome:write specifically. A read-only monitoring key can therefore be granted only pricing.publish.outcome:read.
3

Run your connector

Your service holds the key and calls MerchantOps over HTTPS with:
The key is organization-scoped — your connector only ever sees records that belong to your organization.

Poll for pending publishes

Request the records routed to your target that haven’t been reported yet:
The response lists up to limit records with the fields you need to publish them:
Records are deduplicated by outcome state: once you report an outcome for a record, it drops out of this query. A recommended cadence is a poll every 30–60 seconds, backing off to a few minutes when the response is empty.

Report an outcome

For each record, POST back what happened:
string
required
The target you configured (for example custom).
string
required
One of success, failed, or skipped. Report success or failed for every record — a skipped outcome is stored on the record but does not count toward the batch’s progress. If you decided not to write a record into your system, report it as failed with an error explaining why.
number
The HTTP status or internal result code your system returned. Helps debugging.
number
How long your call took.
number
1 for a first-try success; higher if you retried.
string
A short failure summary — shown in the publish status UI when the status is failed.
string
The record’s ID in your downstream system, if it has one.
The response confirms what was recorded:
batch_final_status is currently always null on the poll path. It is meant to flip to "published" or "partial" when your outcome is the one that finishes the batch, but a batch published to a poll target does not roll up to a final state today — it stays in its publishing state even after every record has reported. Your per-record outcomes are recorded correctly and are visible in publish status; the batch-level rollup is not.Do not use batch_final_status as your completion signal. Track completion on your own side (count the records you polled versus the outcomes you delivered), and contact your MerchantOps contact if a batch needs to be moved out of its publishing state.

Retries and idempotency

Your connector owns its retry logic entirely. A record stays pending until you POST a terminal outcome, so if your connector crashes mid-run, the record is still pending on the next poll — just pick it up again.
Outcome POSTs are not idempotent. Every success or failed POST advances the batch’s published/failed counts, so re-sending an outcome you may already have delivered inflates those counts and the progress on the publish job.Record locally that an outcome has been delivered, before you consider the record done, and do not blindly re-POST after an ambiguous response (a timeout, a dropped connection). If a POST may not have landed, let the normal loop handle it: an outcome that was never recorded comes back as pending on a later poll, so you can publish and report it again then, instead of re-POSTing out of band.Don’t infer delivery from a record’s absence in one poll response. pending-publishes is capped by limit and has no cursor, so on a batch larger than limit a record can be missing from a page simply because it didn’t fit.

A minimal connector

The whole contract fits in one loop:

What you get

  • Isolation. Your connector talks to MerchantOps only over HTTPS. No shared infrastructure, no special client libraries.
  • Org scoping. Your key can only read and update your organization’s records — there’s no way to affect another tenant.
  • The same monitoring surface. Your publishes appear in publish status alongside built-in connectors: per-target breakdown, per-record outcomes, and job progress. The batch-level rollup is the one exception — see the batch_final_status warning above.
Rate-limit yourself to roughly two polls per second per connector. Running multiple connectors for the same target at once can hand you duplicate records (there is no per-worker record lease yet), so run a single connector per target for now.

Next steps

VTEX (reference connector)

A worked example of a built-in connector for catalog and pricing.

Reading publish status

See where your connector’s outcomes show up.