Send your first product
Six steps, about twenty minutes, ending with a product live on BrandsGateway. Tick each one off as you go — the page remembers where you stopped.
Get your three values
Everything below uses the same credential. There is no second key to chase.
BrandsGateway gives you a vendor ID, a username, and an application password. The same three sign you in to this portal.
export BG_VENDOR_ID=143
export BG_USER=YourUsername
export BG_APP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx'
401. Full detail in
Authentication.
Prove the connection works
Two checks before you write any integration code: that the portal answers, and that your credentials are the right ones. Neither changes anything.
curl -s -X POST 'https://vendors.brandsgateway.com/api/v1/validate' \
-H 'Content-Type: application/json' \
-d '{ "type": "simple", "name": "Connection test" }'
valid: false and a list of
errors — that payload is deliberately incomplete. Any
answer at all proves the portal is reachable and the validator is
running.
Now check the credentials themselves: sign in at vendors.brandsgateway.com/login with the same three values. The portal verifies them against app.brandsgateway.com and keeps no account of its own, so a successful sign-in is proof the credential works for the API too.
Describe the product in your own words
You never look up a BrandsGateway ID. You send the words your feed already uses, and we map them.
A payload has two halves. values is your vocabulary —
brand, colour, category, in whatever language your system already
says them. product is everything only you know: name,
images, prices, stock, barcode.
{
"values": {
"brand": "Gucci",
"gender": "Women",
"color": "Brown",
"material": "Calf Leather",
"country": "Italy",
"category": "Bags Women",
"group": "Shoulder Bags",
"condition": "New with tags"
},
"product": {
"type": "simple",
"name": "Leather Shoulder Bag",
"sku": "SKU12345",
"images": [
{
"src": "https://cdn.yourshop.com/12345_1.jpg"
},
{
"src": "https://cdn.yourshop.com/12345_2.jpg"
}
],
"regular_price": "1250.00",
"sale_price": "875.00",
"stock_quantity": 3,
"manage_stock": true,
"global_unique_id": "4068849312345",
"meta_data": [
{
"key": "_external_id",
"value": "SKU12345"
},
{
"key": "_vendor_cost",
"value": "610.00"
},
{
"key": "_vendor_sku",
"value": "SKU12345_V"
}
]
}
}
values.country and values.material become
_bgorigincountry and _bgmaterial;
values.brand becomes a brands ID, and so
on. Those IDs differ between installations, so hardcoding them is a
trap that breaks the day you point at a different environment. Send
the words and let us resolve them. If you genuinely already hold our
IDs, put them in product and they win — see the
product contract.
What a simple product cannot be accepted without
type
Whether the product is one sellable unit or a parent with sized children. Decides which other fields are required.
If it is sold in more than one size it is variable, even when only one size is currently in stock.
name
The customer-facing title. Shown in the shop and in search.
Must be English. Vendor titles in Italian or French are a common cause of listings being pulled during review.
sku
Your product-level SKU as it appears on BrandsGateway. Must be unique across your catalog.
A collision with an existing SKU fails as woocommerce_rest_product_not_created, which reads like a server error but is not.
fails as woocommerce_rest_product_not_created →brands
The designer or label, as a BrandsGateway brand term ID. Exactly one entry.
Brands are curated. A brand that is not already on the platform cannot be created from a feed — request it first.
fails as missing_brand →categories
The browsable category tree the product appears under. Usually a parent and a leaf.
Categories drive navigation, not filtering. Filtering comes from groups — send both.
fails as missing_category →groups
The merchandising hierarchy. Drives filters, the size axis, and harmonised-code inheritance for customs.
Send the full ancestor chain with the leaf last, e.g. Clothing → Sweaters → Sweatshirts. A container group alone has no size axis.
fails as missing_group_mapping →genders
Men (71575), Women (71576) or Unisex (71577). Three fixed terms.
conditions
Stock condition. Determines whether the item can be returned, so it is never inferred.
New-season stock is New with tags (71354).
attributes
The attribute axes. Colour (id 14) with variation:false, plus — for variable products — the size attribute with variation:true and every size in options.
options holds display labels; variation attributes reference term slugs. They are not interchangeable.
fails as missing_size_mapping →images
Absolute, publicly reachable image URLs. First is the main image; the rest form the gallery.
Only uploaded on create, or on update with force_images=true. Minimum two in practice.
fails as images_below_two →meta_data
The key/value bag carrying identity, origin, cost and traceability. See the meta data reference.
An array of objects, not a plain object. Values must be string, number or boolean — never nested.
fails as missing_meta_key →regular_price
The retail (RRP) price the item sells for at full price.
A string with at most two decimals: "540.00", never 540.
fails as invalid_price_format →sale_price
The price BrandsGateway sells at. Must be strictly below regular_price.
fails as invalid_price_ratio →stock_quantity
Units available. Your feed is authoritative — each sync overwrites this value.
manage_stock
Always true. Without it BrandsGateway cannot track depletion and the item oversells.
global_unique_id
The EAN/barcode. Unique across the entire marketplace, not just your catalog.
A conflict means another vendor already lists this exact item. That is resolved commercially, not by retrying.
fails as product_invalid_global_unique_id →Check it before you send it
The same rules the endpoint enforces, with nothing written and nothing stored. Edit the payload below and press check.
Try breaking it on purpose: set sale_price above
regular_price, or change brand to
something we will not know. Both are the two most common rejections,
and both are cheaper to meet here than at 3am in a cron job.
Send it
One call creates and updates. The ID in the URL is yours, and it is how we recognise the same product next time.
curl -s -X PUT 'https://vendors.brandsgateway.com/api/v1/products/SKU12345' \
-u "$BG_USER:$BG_APP_PASSWORD" \
-H "X-Vendor-Id: $BG_VENDOR_ID" \
-H 'Content-Type: application/json' \
-d @product.json
201 Created
{ "sent": true, "accepted": true, "outcome": "created",
"externalId": "SKU12345", "warnings": [] }
200 with
"outcome": "updated" instead of
201. That is the proof your ID round-trips — and what
stops your catalog duplicating itself on every sync.
404 — this catches almost everyone once. It
must also be stable: change it later and we create a second product.
When a value does not map
422
{ "sent": false,
"message": "Some of your values do not map to BrandsGateway yet. Nothing was sent.",
"unresolved": [
{ "field": "material", "value": "VISCOSA RICICLATA",
"suggestions": [ { "name": "Viscose", "confidence": 0.81 } ] }
] }
Nothing is ever guessed — a wrongly-mapped brand is worse than a rejected product, because nobody notices. Your attempt is recorded for us automatically, so the value lands in the BrandsGateway mapping queue without you having to report it. Once added, it works for every product that uses it from then on.
Take a product out of stock
Products you stop sending do not disappear on their own. Send the stock change, the same way you sent the product.
Nothing is ever deleted. A product you no longer carry goes to zero
stock: re-send it with stock_quantity: 0. It keeps its
page and its history, and the day it returns, the same call with a
real quantity republishes it.
curl -s -X PUT 'https://vendors.brandsgateway.com/api/v1/products/SKU12345' \
-u "$BG_USER:$BG_APP_PASSWORD" \
-H "X-Vendor-Id: $BG_VENDOR_ID" \
-H 'Content-Type: application/json' \
-d '{ "product": { "type": "simple", "manage_stock": true, "stock_quantity": 0 } }'
200 and outcome: "updated". For a variable
product, any variation you leave out of the payload is zeroed for
you.
That is the whole integration
Two calls, on whatever schedule suits your stock — hourly is common. Everything else is refinement.
- Watch your monitoring — what landed, what did not, and why.
- Send only what changed — most of a catalog does not change between runs.
- Working clients in Node, PHP and Python.
- Error reference — every rejection reason and its fix.
- Test safely before pointing this at your live catalog.