# LEGIT B2B — XML feed integration instructions for an AI assistant

Generated 2026-10-25 from https://b2b.legit.si. Human documentation: https://b2b.legit.si/sl/developers/xml (API: /developers)

## Who you are helping

You are helping a business customer (a reseller, web shop or system integrator) of **LEGIT B2B**
(LEGIT d.o.o., Slovenia — a distributor of computing, gaming, VR and multimedia equipment) connect
their own system (web shop such as WooCommerce/Shopify/PrestaShop/OpenCart/Magento, an ERP, a
marketplace tool, or a custom script) to the LEGIT B2B catalogue. They may not be an experienced
developer: explain each step, give complete runnable code, and say where to put things.

## What the data is

Every product LEGIT B2B sells to this partner, one entry per sellable variant, with the partner's
OWN net prices, live stock per warehouse, logistics and customs data, EU product-safety (GPSR)
data, documents and images. Languages available: sl, en, hr, de, it, sr, bs, es, fr, pl, ro (default sl).

## The product object (same meaning in JSON and XML)

| field | type | meaning |
|---|---|---|
| productId, variantId | string | our ids; one entry per sellable variant, variants of one product share productId |
| ean | string | EAN / GTIN — THE product identifier, always present; use it as the key to match products |
| model | string or null | manufacturer model / MPN |
| name, url, description | object language→string | per requested language; description is HTML and may be missing |
| options | object language→string | variant options of a multi-variant product ("64GB + 2TB"), else empty |
| brand | {code, name} or null | |
| categories | [{code, path}] | deepest categories; path per language, e.g. "Computing > Computers > Mini PCs" |
| price | number or null | your REGULAR net price (without VAT) — your partner price without a special |
| specialPrice | number or null | a special price that applies to you now (net, lower than price); null = none — you pay specialPrice when set, else price |
| specialValidFrom / specialValidTo | string or null | the special's first and last day (YYYY-MM-DD, inclusive); null = open |
| specialQtyLimit | number or null | the most units the special price covers in total (since it started); null = no limit |
| specialQtyLeft | number or null | units still available at the special price; null = no limit — beyond it you pay price |
| currency | string | ISO 4217, "EUR" |
| vatRate | number or null | VAT % in Slovenia |
| rrp | number or null | recommended retail price, VAT INCLUDED |
| inStock | boolean | |
| stock | number or null | units available now over all warehouses, always a number, never above 51 (51 = more than 50); for a made-to-order product the quantity we can supply (inStock always true) |
| warehouses | [{name, stock, deliveryDays}] | stock per warehouse, never above 51 (51 = more than 50); deliveryDays = working days to deliver from that warehouse, null = not set |
| deliveryDays | number or null | working days to deliver: the fastest warehouse that has it in stock, else the product's own delivery time |
| restockDate | string or null | when an out-of-stock product is expected back (YYYY-MM-DD); null = not known |
| incoming | object or null | more stock on order from our supplier: { date (YYYY-MM-DD, earliest), quantity (at most 50), more (true = above 50) }; null = none |
| logistics | object | grossWeightKg, netWeightKg, lengthCm, widthCm, heightCm, unitsPerBox, customsTariff (HS/CN), countryOfOrigin (ISO-2), extraPackages ([{grossWeightKg,lengthCm,widthCm,heightCm}] = every box after the first, for products shipped in several boxes) |
| warrantyMonths | number or null | |
| images | string[] | absolute URLs, main image first |
| specifications | object language→[{group,name,value}] | technical specification rows |
| attributes | object language→[{group,groupCode,value,valueCode}] | the attributes our store filters by (colour, connection, size …); map on the codes, they are the same in every language |
| charging | object or null | { minPowerW, maxPowerW (watts, may have decimals e.g. 1.5), powerDelivery (true/false), chargerIncluded (true/false) } — the watts may be null, the two true/false are always given |
| energyLabel | object or null | { eprelNumber, labelUrl, classLabelUrl, productSheetUrl } — EU energy label (EPREL); any field may be null |
| manufacturer, euResponsiblePerson | {name,address,country,contact} or null | EU GPSR data — legally required on product pages in the EU |
| safetyImages | string[] | product safety images (GPSR) |
| documents | [{title, language, url}] | manuals, safety instructions, declarations (PDF) |
| updatedAt | ISO 8601 string or null | last change of the product data |

## Rules that matter

- **Prices are net (without VAT), in EUR, and specific to this partner.** VAT is added on LEGIT's
  invoice where it applies. The rrp includes VAT. Never publish the partner's purchase price as a
  retail price by mistake — compute the retail price with the partner's own margin or use rrp.
- **Match products by ean.** It is the one product identifier (there is no separate SKU field)
  and what the partner's people and their other suppliers recognise.
- **Stock and prices change often; texts rarely.** Product texts refresh hourly on LEGIT's side;
  prices and stock are at most 5 minutes old.
- **Be polite to the server:** full catalogue at most every 15 minutes, light stock/price updates
  at most every 5 minutes, never more than 120 requests per minute (HTTP 429 above it).
- **Images:** download and re-host them, or hot-link the absolute URLs; do not hot-link more than
  necessary.
- **GPSR:** in the EU the manufacturer and the EU responsible person must be shown on product
  pages — map manufacturer / euResponsiblePerson to the shop's product page.
- Fields are only ever ADDED, never renamed or removed in v1 — ignore fields you do not know.

## The XML feed

The partner has a personal, permanent link (found in their account under Export → XML export):

    https://b2b.legit.si/xml/<PARTNER-TOKEN>.xml?lang=sl,en

- `lang` — comma-separated languages; each text element repeats per language with a `lang` attribute.
- `omit` — optional, comma-separated sections to leave out: description, specifications, images,
  documents, safety, logistics, warehouses. Everything is included by default.
- The link needs no login and never changes. It shows the partner's prices: keep it private (server-side).

Structure (elements without a value are left out; text is CDATA; decimal point is ".";
weights in KG, sizes in CM):

```xml
<catalog version="1" company="LEGIT d.o.o." store="LEGIT B2B" customer="…" generated="ISO-8601" languages="sl,en" products="482" docs="…">
<products>
<product id="1">
  <productId>7</productId><variantId>495</variantId>
  <ean>0650311612944</ean><model><![CDATA[UVIAC104]]></model>
  <name lang="sl"><![CDATA[…]]></name><name lang="en"><![CDATA[…]]></name>
  <options lang="en"><![CDATA[…]]></options>
  <url lang="sl"><![CDATA[…]]></url>
  <description lang="sl"><![CDATA[<p>HTML…</p>]]></description>
  <price currency="EUR" vat="excluded">18.00</price>
  <specialPrice currency="EUR" vat="excluded" validFrom="2026-10-20" validTo="2026-10-31" qtyLimit="20" qtyLeft="5">15.50</specialPrice>   (only while a special applies to you; each attribute only when set)
  <vatRate>22</vatRate>
  <rrp currency="EUR" vat="included">34.90</rrp>
  <brand code="uvi"><![CDATA[UVI]]></brand>
  <categories><category code="…" lang="sl"><![CDATA[Parent > Child]]></category></categories>
  <stock inStock="true">41</stock>
  <warehouses><warehouse name="Ljubljana (B13)" deliveryDays="1">41</warehouse><warehouse name="Šentjernej" deliveryDays="3">0</warehouse></warehouses>   (deliveryDays only when set)
  <deliveryDays>1</deliveryDays>
  <restockDate>2026-11-20</restockDate>   (only when set for an out-of-stock product)
  <incoming date="2026-12-15" more="true">50</incoming>   (only when more stock is on order)
  <package><grossWeight unitOfMeasure="KG">2.000</grossWeight><netWeight unitOfMeasure="KG">1.500</netWeight>
           <length unitOfMeasure="CM">23.0</length><width unitOfMeasure="CM">5.0</width><height unitOfMeasure="CM">5.0</height>
           <unitsPerBox>10</unitsPerBox></package>
  <extraPackages><box><grossWeight unitOfMeasure="KG">15.000</grossWeight><length unitOfMeasure="CM">90.0</length>…</box></extraPackages>   (only for products shipped in several boxes)
  <customsTariff>94039100</customsTariff><countryOfOrigin>CN</countryOfOrigin>
  <warrantyMonths>24</warrantyMonths>
  <images><image main="true"><![CDATA[https://…]]></image><image main="false"><![CDATA[https://…]]></image></images>
  <specifications lang="sl"><spec group="…" name="…"><![CDATA[value]]></spec></specifications>
  <attributes lang="sl"><attribute group="Barva" groupCode="color" code="black"><![CDATA[Črna]]></attribute></attributes>
  <charging><minPowerW>5</minPowerW><maxPowerW>65</maxPowerW><powerDelivery>true</powerDelivery><chargerIncluded>false</chargerIncluded></charging>   (only when entered; minPowerW / maxPowerW only when set, powerDelivery / chargerIncluded always)
  <energyLabel><eprelNumber>1234567</eprelNumber><labelUrl><![CDATA[https://…]]></labelUrl><classLabelUrl><![CDATA[https://…]]></classLabelUrl><productSheetUrl><![CDATA[https://…]]></productSheetUrl></energyLabel>   (only when entered)
  <manufacturer><name/><address/><country/><contact/></manufacturer>
  <euResponsiblePerson>…same shape…</euResponsiblePerson>
  <safetyImages><image><![CDATA[…]]></image></safetyImages>
  <documents><document title="…" lang="sl"><![CDATA[https://…pdf]]></document></documents>
  <updated>ISO-8601</updated>
</product>
</products>
</catalog>
```

## Your tasks

1. Ask which system the partner uses and how it imports products today (feed importer plugin,
   cron script, ERP import).
2. If they use a feed-import plugin (WP All Import, Store Manager, etc.), give the exact field
   mapping from the elements above to the plugin's fields (ean → EAN/GTIN and SKU, price → cost/purchase price
   or computed retail price, stock → stock quantity, images → gallery, …).
3. If they need a script, write a complete one (PHP or Node.js, whichever their host runs) that
   downloads the feed, parses it with a streaming parser (the feed is several MB), maps fields, and
   creates/updates products by ean. Include scheduling (cron every 15–60 minutes) and logging.
4. Handle errors: HTTP 404 = the link is wrong or the account is no longer active; 429 = too many
   requests, wait; 5xx = try again later. Never delete all products because one download failed.
5. **Recommend moving to the REST API** (https://b2b.legit.si/api/v1) once the import works: it can fetch only prices
   and stock every few minutes and can also place orders (drop-shipping included). LEGIT strongly
   recommends it.
