production writes → app.brandsgateway.com Examples on this page target the live marketplace.
BG Vendor Integration
Menu
Step 1 · guided

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.

Step 1

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.

set them once
export BG_VENDOR_ID=143
export BG_USER=YourUsername
export BG_APP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx'
The application password is not your login password. It is generated for you and shown in spaced groups — the spaces are cosmetic, and you can keep or strip them. A login password will always give you 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.

reachability test — needs no credential
curl -s -X POST 'https://vendors.brandsgateway.com/api/v1/validate' \
  -H 'Content-Type: application/json' \
  -d '{ "type": "simple", "name": "Connection test" }'
You should see
A JSON object with 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.

“Username or application password is not correct” means exactly that — and it must be an application password, not your login password. A message naming your vendor ID means the credential is valid but does not belong to that vendor; ask BrandsGateway to attach your user to it. Your first authenticated API call is the product write in step 5.

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.

product.json
{
  "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"
      }
    ]
  }
}
Why there are no IDs in here
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
"simple" | "variable"
Required

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
string
Required

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
string
Required

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
[{ id: integer }]
Required

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
[{ id: integer }]
Required

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
[{ id: integer }]
Required

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
[{ id: integer }]
Required

Men (71575), Women (71576) or Unisex (71577). Three fixed terms.

conditions
[{ id: integer }]
Required

Stock condition. Determines whether the item can be returned, so it is never inferred.

New-season stock is New with tags (71354).

attributes
[{ id, variation, options }]
Required

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
[{ src: uri }]
Required

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
[{ key, value }]
Required

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
string
Simple only

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
string
Simple only

The price BrandsGateway sells at. Must be strictly below regular_price.

fails as invalid_price_ratio →
stock_quantity
integer
Simple only

Units available. Your feed is authoritative — each sync overwrites this value.

manage_stock
boolean
Simple only

Always true. Without it BrandsGateway cannot track depletion and the item oversells.

global_unique_id
string
Simple only

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 it — nothing is sent

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.

This is the call to put in your CI. It needs no credentials and has no rate limit, so it can run on every build — that is what stops a feed change from rejecting thousands of products overnight. The full-page version is the browser validator.

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.

PUT /api/v1/products/{id}
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
You should see
accepted
201 Created
{ "sent": true, "accepted": true, "outcome": "created",
  "externalId": "SKU12345", "warnings": [] }
Run it a second time and you get 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.
Your product ID must be alphanumeric. A hyphen returns 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

nothing was sent
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.

PUT /api/v1/products/{id}
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 } }'
You should see
200 and outcome: "updated". For a variable product, any variation you leave out of the payload is zeroed for you.
Send the stock change per product, and only for products you mean to zero. A whole-catalog reconciliation call — one list of everything you still carry, with a guard against zeroing more than half of it by accident — is not available yet, so nothing here zeroes a product you did not name. See the lifecycle.

That is the whole integration

Two calls, on whatever schedule suits your stock — hourly is common. Everything else is refinement.

Next — step 2: the full product contract
You have a product live. The product contract is every field you can send, what each must contain, and what breaks without it.