Your own system (ERP or warehouse)
Make your own system the source of truth for price and stock: mint a credential, push changes, verify InventoryChanged webhooks, reconcile with ModifiedSince.
This page is the whole recipe for making your own system — an ERP, a WMS, a spreadsheet on a timer — the source of truth for price and stock. Follow it top to bottom and you will have a two-way integration: your system pushes changes in, and hears about every change it did not make.
You need an account with the API enabled. Everything below is plain HTTPS and HMAC; there is no SDK to install.
1. Mint a credential
In MarketplaceHub, open the account menu, then API → Credentials. Create a credential with these permissions:
PricingWrite— to push price and stock.InventoryRead— to read back and reconcile.WebhooksWrite— to manage your subscription.
The secret is shown once and stored hashed. Copy it before leaving the page.
Then exchange it for a token, as Getting started describes:
curl -X POST https://identity.marketplacehub.com/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=mh_YourClientId" \
-d "client_secret=YourSecret" \
-d "scope=InventoryRead PricingWrite WebhooksWrite"
2. Tell MarketplaceHub your system owns price and stock
Open Data Flow in the app, press Change on Price, and choose My own system. Do the same for Stock. See Source of truth for what that changes.
This step is not optional and it is not cosmetic. Until you do it, a marketplace may still own those values, and anything you push will be overwritten the next time that marketplace syncs — the API will tell you so in the response, but it will still happen.
3. Push price and stock
curl -X POST https://api.marketplacehub.com/api/inventory/price-quantity \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Items": [
{ "Sku": "WIDGET-RED-L", "Price": 24.99, "Quantity": 12 },
{ "Sku": "WIDGET-BLU-L", "Quantity": 0 }
]
}'
- Up to 500 items per request. The changes are applied while you wait, so you are told which SKUs landed.
PriceandQuantityare both optional. Omitting one leaves it alone —nullmeans "not provided", never "set to zero". Send"Quantity": 0when you mean nothing in stock.- A SKU repeated in one request collapses to its last occurrence before anything is written.
PropagateToListingsdefaults totrue, which schedules the outbound write to every marketplace. Set it tofalsewhen you are back-filling values the marketplaces already hold.
The response carries one result per distinct SKU, and a Warnings list. A warning here is the drift message described in Source of truth: your write was applied, and something else owns that data and will overwrite it. If step 2 was done properly you will never see one — so treat any warning as a configuration problem, not as noise.
4. Hear about changes you did not make
Subscribe to InventoryChanged:
curl -X POST https://api.marketplacehub.com/api/webhooks/Create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Url": "https://your-erp.example.com/hooks/marketplacehub",
"EventTypes": ["InventoryChanged"]
}'
Keep the Secret from the response. It is shown once and cannot be retrieved.
The event fires whenever price or stock actually moves in MarketplaceHub through the price-and-stock seam, whatever moved it — a file import, a marketplace sync, or your own push. See Webhooks for the body.
Ignoring your own push
Your writes fire the event too, so a naive receiver will apply its own change back to itself. The body tells you who wrote it:
"source": { "writer": "MerchantPush", "channel": "Api", "userPlatformId": null }
writer is MasterMirror when a marketplace sync produced the change and MerchantPush when somebody did something. channel says which path wrote: Api for a call to this API, FileImport for a price-and-stock file import, Marketplace for a sync, Assistant for a change the merchant confirmed in MarketplaceHub's Agent Bot, Mcp for a change an AI assistant made through MarketplaceHub's MCP tools, Rollback for a change a person made by undoing an earlier batch, Manual for a stock figure the merchant typed on MarketplaceHub's Products screen. Ignore channel == "Api" and act on everything else — that drops your own pushes and still hears about the merchant's file imports, Agent Bot changes and assistants' changes, which writer alone could not tell apart.
Two cases still deserve a value comparison: a second system writing through the API arrives as Api too, and a delivery can be retried. Apply the event only where the incoming figure differs from what your system already holds — that makes the receiver idempotent either way.
5. Verify the signature
Every delivery carries these headers:
| Header | Carries |
|---|---|
X-MH-Signature | v1= followed by the lower-case hex HMAC-SHA256. |
X-MH-Timestamp | Unix seconds at the moment of signing. |
X-MH-Event | The event name, e.g. InventoryChanged. |
X-MH-Delivery-Id | Stable across retries of the same delivery — use it to deduplicate. |
X-MH-Attempt | 1 for the first try, then 2, 3… |
The signature covers timestamp + "." + body, not the body alone. That is what stops a captured delivery being replayed at you later.
import hmac, hashlib, time
def verify(secret, timestamp_header, raw_body, signature_header, tolerance=300):
if abs(time.time() - int(timestamp_header)) > tolerance:
return False # too old, or too far in the future
signed = f"{timestamp_header}.{raw_body}"
expected = "v1=" + hmac.new(secret.encode(), signed.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)
- Use the raw request body, byte for byte. Parsing the JSON and re-serialising it will change the bytes and the signature will not match.
- Compare in constant time (
compare_digestabove), not with==. - Reject anything more than 300 seconds old. We use the same tolerance.
Answer 2xx quickly. Do the work afterwards, on your own queue — a receiver that finishes its processing before replying is a receiver that times out under load and gets retried.
6. Reconcile, because delivery is best-effort
Webhooks are a notification, not a guarantee. A delivery is retried on failure, but six consecutive failures disable the subscription — and a subscription that is off is silent, not noisy. If your endpoint is down for a maintenance window longer than the retries cover, you will miss changes and nothing will tell you.
So run a reconciliation pass on a schedule. POST /api/products/List takes a ModifiedSince cursor:
curl -X POST https://api.marketplacehub.com/api/products/List \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "ModifiedSince": "2026-09-07T00:00:00Z", "Limit": 200, "Offset": 0 }'
Supplying ModifiedSince also orders the page oldest change first, which is what makes the walk resumable: take the LastModifiedDate of the last product on the page and use it as the next call's ModifiedSince. Without an order, a product changed mid-walk moves between pages and can be read twice or skipped — and the one it skips is exactly the row the pass exists to repair.
A reasonable rhythm: act on webhooks for freshness, and reconcile hourly with a ModifiedSince a little older than your last successful pass. Overlap is harmless if your receiver is idempotent, and step 4 already made it so.
What the response carries
Each product carries Sku, Quantity, Price, LastModifiedDate and its Listings — so a reconciliation pass can check both of the things your system owns.
Price is nullable, and the null means something: it is the price MarketplaceHub holds, and a product that has never had one has null rather than 0. Do not read a null as free.
It is also the hub's figure, not what a marketplace is currently listed at. A channel that carries a price of its own keeps it — MarketplaceHub's price is the fallback beneath it — so a null here does not mean your listings have no price, and a value here does not mean every listing shows it. If you need per-listing figures, the marketplace is the place to read them.
Check the subscription's health while you are there — POST /api/webhooks/List returns ConsecutiveFailures, LastSuccessDate and whether it is still active.
Putting it together
- Your system pushes a change →
POST /api/inventory/price-quantity. - MarketplaceHub stores it and schedules the write to every marketplace.
- Anything that changes price or stock — including your push — fires
InventoryChanged. - Your receiver verifies the signature, ignores
channel == "Api", and applies the rest. - An hourly
ModifiedSincepass closes anything the webhooks missed.