production writes → app.brandsgateway.com Examples on this page target the live marketplace.
BG Vendor Integration
Menu
Operating it

Error reference

Every rejection carries a stable machine code. The codes below are the complete set — grouped by the stage that produces them, because the stage tells you who fixes it and where.

Codes are stable; messages are not
Branch on code. The human message may be reworded or gain detail at any time; the code will not change meaning. The monitoring dashboard groups by exactly these codes.

The four stages

  • Mapping — a value in your feed has no BrandsGateway equivalent. Fixed once, in your lookup table, for every product with that value.
  • Schema — the payload is structurally wrong. Fixed in your transform, and catchable before you send via the validator.
  • Business rule — the payload is well-formed but the product is not sellable as described. Usually a data problem at your source.
  • BrandsGateway API — the platform refused the write. Often commercial rather than technical.

Mapping

missing_brand Mapping rejected brands[].id

Brand could not be mapped

The brand name in your feed does not resolve to an existing BrandsGateway brand term.

Fix — Look the brand up in the Brands reference and send its numeric term ID. If the brand genuinely does not exist on BrandsGateway yet, request it — do not invent an ID.

missing_category Mapping rejected categories[].id

Category could not be mapped

Your product's category path has no mapping to a BrandsGateway category.

Fix — Map your category string to a BrandsGateway category term ID once; the mapping then applies to every product in that category.

missing_group_mapping Mapping rejected groups[].id

Product group has no attribute set

The product group you sent exists, but it carries no size attribute, so variations cannot be built.

Fix — Pick a leaf product group that has a size axis (e.g. Sweaters, Sneakers), not a top-level container group.

missing_color Mapping rejected color

Colour could not be mapped

The colour value is empty, or it does not match any BrandsGateway colour.

Fix — Send one of the BrandsGateway colour names (e.g. "Beige", "Black") in `color` and mirror it in the colour attribute (id 14).

missing_material Mapping rejected meta_data._bgmaterial

Material could not be mapped

The composition string contains a material BrandsGateway does not recognise.

Fix — Split the composition into individual materials and map each to a BrandsGateway material name. Percentages are stripped before matching.

missing_country Mapping rejected meta_data._bgorigincountry

Country of origin could not be mapped

The country value is missing or is not a recognised country.

Fix — Send a two-letter ISO country code, e.g. `IT`, `CN`, `PT`.

missing_size_mapping Mapping rejected variations[].attributes

One or more sizes could not be mapped

A size in your feed has no corresponding term in the size attribute taxonomy you selected.

Fix — Use the size slugs of the attribute you declared. If your sizing system differs (e.g. FR vs IT), pick the matching attribute rather than translating values yourself.

missing_gender Mapping rejected genders[].id

Gender could not be mapped

The gender word in your feed does not resolve to a BrandsGateway gender term.

Fix — Send the numeric term ID from the Genders reference. Gender IDs are per-install, not platform constants — read them from `/api/v1/reference/genders` rather than copying an ID out of a document. Feed words like "Donna", "Woman" and "Ladies" all mean the same term; map them once on your side.

missing_condition Mapping rejected conditions[].id

Condition could not be mapped

The product condition you sent has no matching BrandsGateway condition term.

Fix — Look it up in `/api/v1/reference/conditions` and send the numeric term ID. Like genders, conditions are per-install. Omit the field entirely if all your stock is new and the install has no explicit "new" term.

Schema

schema_validation_error Schema rejected

Payload failed schema validation

The product did not satisfy the BrandsGateway product JSON Schema. The response names the exact path and rule.

Fix — Run the payload through the Validator before sending. Every finding it reports is a hard block.

missing_required_field Schema rejected

A required field is absent

One of the always-required fields, or a conditionally-required field for this product type, is missing.

Fix — Simple products additionally require price, stock, `manage_stock` and `global_unique_id`. Variable products additionally require `variations`.

missing_meta_key Schema rejected meta_data

A required meta_data key is absent

`meta_data` is present but does not contain a key the schema requires for this product type.

Fix — `meta_data` is an array of `{ key, value }` objects. Both product types need `_bgorigincountry` and `_external_id`; simple products also need `_vendor_cost` and `_vendor_sku`.

invalid_price_format Schema rejected regular_price / sale_price

Price is not a valid decimal string

Prices must be strings matching `^[0-9]+(\.[0-9]{1,2})?$` — no currency symbol, no comma decimal separator, no more than two decimals.

Fix — Send `"280.00"`, not `280`, `"280,00"` or `"€280"`.

invalid_type Schema rejected

Field has the wrong JSON type

A value is the right idea but the wrong type — most often a numeric price, a stringified stock count, or a taxonomy name where an integer ID is expected.

Fix — Prices are strings; stock and all taxonomy IDs are integers.

invalid_enum Schema rejected

Value is not one of the allowed options

The field accepts a fixed set of values and yours is not in it. The error message lists every value that is allowed.

Fix — Copy one of the listed values exactly — they are case-sensitive. The usual cause is a translated or title-cased variant (`"Simple"` for `"simple"`).

pattern_mismatch Schema rejected

Value does not match the required format

The field is constrained by a regular expression and your value does not satisfy it. The message quotes the pattern.

Fix — Check for stray whitespace, punctuation or unit suffixes. `external_id` is the common one: it must be alphanumeric only — a hyphen makes the product URL 404 upstream, so it is rejected here first.

empty_string Schema rejected

Field is present but blank

The field was sent as an empty string. That is not the same as omitting it, and it is never valid for a required field.

Fix — Send a real value, or leave the key out entirely if the field is optional. A blank `name` or `sku` usually means an upstream lookup in your own feed returned nothing — fix it there rather than sending the blank through.

empty_array Schema rejected

Array needs at least one entry

A list that must not be empty was sent as `[]` — most often `images`, `brands`, `categories`, `groups` or `attributes`.

Fix — Populate it, or omit the key if the field is optional. An empty `attributes` on a variable product is the frequent case, and it means your size axis was never built.

empty_object Schema rejected

Object needs at least one key

An object that must carry at least one key was sent as `{}`.

Fix — Usually `variations[].attributes`, which is an object keyed by taxonomy slug — e.g. `{ "pa_italian-size-women": "m" }`. An empty one means the size slug was not resolved before you sent the product; call `/api/v1/resolve` first.

out_of_range Schema rejected

Number is below the allowed minimum

A numeric field is smaller than the schema permits. The message states the limit.

Fix — Most often a negative `stock_quantity`. Send `0` for out of stock — negative stock is not how you remove a product, `POST /products/{id}/reset` is.

invalid_format Schema rejected images[]

Value is not a valid URI, date or email

The field declares a format and the value does not parse as one. Almost always an image URL.

Fix — Image URLs must be absolute and publicly reachable — `https://…`, not a relative path, not a `file://` URL, and not behind a login. BrandsGateway fetches them server-side, so anything your CDN blocks by referer or IP will fail here.

Business rule

missing_ean Business rule rejected global_unique_id

No EAN / barcode

The product (or a variation) has no `global_unique_id`. BrandsGateway identifies stock across the marketplace by barcode.

Fix — Supply a real EAN per sellable unit — per product for simple, per size for variable. Do not synthesise one.

invalid_price_ratio Business rule rejected sale_price

Sale price is not below retail price

`sale_price` is greater than or equal to `regular_price`. BrandsGateway sells below RRP by definition.

Fix — Send the true retail price in `regular_price` and your BrandsGateway price in `sale_price`.

no_valid_variations Business rule skipped variations

No sellable variation remained

Every variation was dropped — typically because each one failed the price check or had no stock.

Fix — Only send variations that are genuinely sellable. A product with nothing left to sell should be reported as out of stock instead.

images_below_two Business rule rejected images

Fewer than two images

The product carries fewer than two usable images.

Fix — Provide at least two publicly reachable, absolute image URLs.

duplicate_external_id Business rule skipped meta_data._external_id

Duplicate external ID in the same feed

Two products in one submission share an `_external_id`. Both are dropped, because the platform cannot tell which is authoritative.

Fix — `_external_id` must be unique and stable per product across your whole catalog and across time.

invalid_external_id Business rule rejected meta_data._external_id

External ID contains characters BrandsGateway cannot route

BrandsGateway only routes external IDs made of letters and digits. An ID with a hyphen, underscore, space or dot never reaches the product endpoint, and would otherwise come back as a confusing `rest_no_route`.

Fix — Use only A–Z, a–z and 0–9 — `SKU12345`, not `SKU-12345`. The external ID is the product's permanent identity, so pick the format once, before the first send.

missing_variation_attribute Business rule rejected attributes

Variable product has no size axis

A variable product needs at least one attribute marked `variation: true`. Yours has none, so there is nothing for the variations to vary along.

Fix — The size attribute must be `variation: true` and list every size in `options`. Note that the colour attribute (id 14) is `variation: false` on purpose — colour is a property of the product, size is the axis. Having only colour is the usual cause.

duplicate_ean_within_product Business rule rejected variations[].global_unique_id

Two variations share one barcode

The same EAN appears on more than one variation of this product.

Fix — Every size needs its own barcode. BrandsGateway enforces `global_unique_id` uniqueness across the whole platform, so a duplicate inside one product would fail upstream anyway — and would take the rest of the product with it. If you genuinely do not have per-size barcodes, ask BrandsGateway before inventing them.

missing_import_source Business rule partial meta_data._import_source

No `_import_source` meta key

The product carries no marker saying which feed it came from. This is a warning: the product still imports.

Fix — Set `_import_source` to your vendor slug. It costs nothing and it is what makes a product traceable back to your feed when someone is investigating a problem months later.

BrandsGateway API

product_invalid_global_unique_id BrandsGateway API rejected global_unique_id

EAN already in use on BrandsGateway

The barcode is already attached to a different product — often another vendor's listing of the same item. The response names the conflicting product, its vendor and its URL.

Fix — Confirm the EAN is correct. If it is, the item is already on the marketplace under another vendor and BrandsGateway resolves the conflict commercially — not by overwriting.

woocommerce_rest_product_not_created BrandsGateway API rejected

BrandsGateway refused to create the product

The payload passed validation but the platform could not persist it — typically a SKU collision or an unresolvable term reference.

Fix — Check that `sku` is unique across your catalog and that every taxonomy ID you sent exists.

api_error BrandsGateway API rejected

Unclassified API error

BrandsGateway returned a 4xx/5xx that does not map to a known code.

Fix — Retry with backoff. If it persists, open the run in the dashboard and share the run ID with support.

rate_limited BrandsGateway API rejected

Too many requests

You exceeded the per-vendor request rate.

Fix — Keep submissions to roughly two products per second per vendor and retry after the interval named in the response.

Reading an EAN conflict

The barcode conflict is the one rejection that names another vendor. It means the physical item is already on the marketplace, so the response gives you enough to verify that without guessing.

400 response
HTTP/1.1 400 Bad Request

{
  "code": "product_invalid_global_unique_id",
  "message": "Global Unique ID \"8054802812345\" is already in use by
              product #9075486 (Pescara Distribuzione).",
  "data": {
    "status": 400,
    "duplicate_global_unique_id": "8054802812345",
    "attempting_external_id": "4820113077",
    "conflict": {
      "id": 9075486,
      "vendor_id": 72543,
      "vendor_name": "Pescara Distribuzione",
      "url": "https://app.brandsgateway.com/product/...",
      "global_unique_id": "8054802812345"
    }
  }
}

If the EAN in your feed is wrong, correct it and resend. If it is right, the item genuinely exists twice on the marketplace and BrandsGateway resolves that commercially — retrying will not change the outcome.

Handling rejections in your client

record, then decide
// Treat rejections as data, not exceptions. Record every one against
// the run so the dashboard can group them — retrying a mapping failure
// produces the same failure.

const RETRYABLE = new Set(["api_error", "rate_limited"]);

async function submit(payload, run) {
  const res = await send(payload);
  if (res.ok) {
    return record(run, res.status === 201 ? "created" : "updated", payload);
  }

  const body = await res.json();
  record(run, "failed", payload, {
    errorCode: body.code ?? "api_error",
    errorMessage: body.message,
  });

  if (RETRYABLE.has(body.code) && run.attempt < 3) {
    await backoff(run.attempt);
    return submit(payload, { ...run, attempt: run.attempt + 1 });
  }
}
Only two codes are worth retrying
rate_limited and api_error are transient. Everything else is deterministic: the same payload will be rejected the same way forever. Retrying it burns your rate limit and buries the real signal in your telemetry.