Field reference

Full and update feed schemas in XML and JSON: envelope, product fields, format differences and per-product attributes.

All feeds share the same envelope and field semantics. The full feed carries every field below; the update feed carries only the fast-moving subset — the ✓ column — see Update feed. The SKU is the unique identifier for every product — use it as your import key.

Feed envelope

XML

<syntechstock>
  <data>
    <count>2828</count>       <!-- products in this file -->
    <currency>ZAR</currency>  <!-- currency of all prices in this file -->
  </data>
  <stock>
    <product>...</product>
    <product>...</product>
  </stock>
</syntechstock>

JSON

{
  "syntechstock": {
    "count": 2828,
    "currency": "ZAR",
    "products": [ { "sku": "..." }, { "sku": "..." } ]
  }
}

CSV has no envelope — a header row plus one row per product, with currency as a per-row column instead.

Product fields

FieldTypeUpdate feedDescription
skustringUnique product identifier — use as your unique key
namestringProduct name
pricedecimalYour cost price — what you pay Syntech for the item — excluding VAT, in the feed's currency. Always the normal cost; it does not change during a promotion (see Pricing)
rrp_incldecimalRecommended retail price, including VAT
recommended_margindecimalSuggested margin, based on the normal price (see Pricing)
promo_pricedecimalPromotional cost price, excluding VAT — empty (XML) / false (JSON) / N/A (CSV) when not on promotion
promo_starts / promo_endsdatePromotion window (inclusive) — empty (XML) / false (JSON) / N/A (CSV) when not on promotion
cptstockintegerStock on hand — Cape Town
jhbstockintegerStock on hand — Johannesburg
dbnstockintegerStock on hand — Durban
nextshipmentetadateETA of the next incoming shipment — empty when none is scheduled
urlstringProduct page on syntech.co.za
descriptionHTMLFull description — features, specifications, what's in the box
shortdescHTMLShort description
weightdecimalProduct weight in grams0 when not captured
length / width / heightdecimalProduct dimensions in centimetres0 when not captured
featured_imageURLPrimary product image. CSV header: featured image (space, not underscore)
additional_imagesURL listGallery images. CSV header: additional images
all_imagesstringEvery image URL, pipe-separated — convenience field for single-column importers. CSV header: all images
categoriesstringCategory names, comma-separated
categoriesaltstringCategory names, pipe-separated
categorytreestringHierarchy as Parent > Child; multiple trees pipe-separated. Not present in CSV
categorytreealtstringHierarchy as Parent/Child; multiple trees pipe-separated. Not present in CSV
attributescontainerProduct attributes — the set varies per product, see below. In CSV: one column per attribute, from a fixed legacy set
last_modifieddatetime✓ XML/JSONWhen this product's data last changed — useful for change detection on your side. Not present in CSV
currencystring✓ CSV onlyPer-row currency column — CSV has no envelope; in XML/JSON this lives in the envelope

Pricing

price (and promo_price when on promotion) is your dealer cost, excluding VAT — the amount you pay Syntech for the item. rrp_incl is the recommended retail price, including VAT. Worked example: cost 169.00, RRP incl 299.00260.00 excluding VAT (15%), and (260 − 169) ÷ 260 = 35% — the recommended_margin. The margin is calculated against the normal price. Remember to add your margin and VAT when calculating your selling price.

Promotions

price always shows the normal cost — it does not switch to the promo value while a promotion runs. To price correctly, apply this rule in your importer:

If promo_price is populated and today falls between promo_starts and promo_ends (inclusive), use promo_price; otherwise use price.

Always gate on the dates rather than assuming a populated promo_price is currently active. Example: price: 8128.00, promo_price: 7999.00, window 2026-07-302026-08-30 — your cost is 7999.00 during the window and 8128.00 outside it.

Update feed

The update feed shares the envelope above and covers the same full product set (same count), but each product carries only the fast-moving fields marked ✓ — pricing, promos, branch stock, shipment ETA and last_modified. Omitting names, content, images, categories and attributes makes the file dramatically smaller, which is exactly what you want for frequent syncs.

Recommended pattern: import and refresh content from the full feed periodically, then run the update feed on your frequent schedule — match on sku and update only the fields present. Field semantics are identical to the full feed, including the not-on-promotion representations (false in JSON, empty in XML, N/A in CSV).

XML

<product>
  <sku>DM-12</sku>
  <price>169.00</price>
  <rrp_incl>299.00</rrp_incl>
  <recommended_margin>35.00</recommended_margin>
  <promo_price/>
  <promo_starts/>
  <promo_ends/>
  <cptstock>0</cptstock>
  <jhbstock>0</jhbstock>
  <dbnstock>0</dbnstock>
  <nextshipmenteta>2026-10-02</nextshipmenteta>
  <last_modified>2026-08-18 03:25:40</last_modified>
</product>

JSON

{
  "sku": "DM-12",
  "price": 169,
  "rrp_incl": 299,
  "recommended_margin": 35,
  "promo_price": false,
  "promo_starts": false,
  "promo_ends": false,
  "cptstock": 0,
  "jhbstock": 0,
  "dbnstock": 0,
  "nextshipmenteta": "2026-10-02",
  "last_modified": "2026-08-18 03:25:40"
}

CSV

sku,price,rrp_incl,recommended_margin,promo_price,promo_starts,promo_ends,cptstock,jhbstock,dbnstock,nextshipmenteta,currency
DM-12,169.00,299.00,35.00,N/A,N/A,N/A,0,0,0,2026/10/02,ZAR

CSV specifics to note when parsing: fields that are empty (XML) or false (JSON) are the literal string N/A — don't feed them straight into a number parser; dates use YYYY/MM/DD (slashes, not dashes); currency is a per-row column; and there is no last_modified column.

On promotion — XML example

<product>
  <sku>ELA6470GL</sku>
  <price>8128.00</price>
  <rrp_incl>10999.00</rrp_incl>
  <recommended_margin>15.02</recommended_margin>
  <promo_price>7999.00</promo_price>
  <promo_starts>2026-07-30</promo_starts>
  <promo_ends>2026-08-30</promo_ends>
  <cptstock>0</cptstock>
  <jhbstock>0</jhbstock>
  <dbnstock>0</dbnstock>
  <nextshipmenteta/>
  <last_modified>2026-08-18 13:11:16</last_modified>
</product>

Note price still shows the normal 8128.00 even though the promotion is active, and nextshipmenteta is empty when no shipment is scheduled.

Delisted products

End of life or delisted products drop out of the feeds entirely, there is no status flag or tombstone record. To detect removals, diff the feed's SKU set against your catalogue after a sync and deactivate/draft anything no longer present.

Before acting on absences, validate that your download parsed completely by comparing the number of products you parsed against the envelope's count . A truncated or partial file should never trigger mass deactivation on your store.

Format differences

Field names match across formats, with the CSV exceptions noted here and in the table above; the shapes differ:

XMLJSONCSV
Envelope<data> block, products as <stock><product> elementscount / currency at top level, products arraynone — header row + one row per product; currency is a per-row column
Numbers (prices, stock, dimensions)text contentnative JSON numberstext
Not on promotionempty <promo_price/> elementsfalse (boolean) — test for false, not nullliteral string N/A
DatesYYYY-MM-DDYYYY-MM-DDYYYY/MM/DD
Image fieldsfeatured_image etc., underscoresfeatured_image etc., underscoresfeatured image, additional images, all imagesspaces in the headers
attributeskebab-case elements, CDATA-wrapped — completeobject of kebab-case keys → string values — completeone column per attribute, fixed legacy set — incomplete (see below)
description / shortdescCDATA-wrapped HTMLHTML stringHTML inside quoted cells — use a proper CSV parser, never split on commas
categorytree / categorytreealtpresentpresentnot present
last_modifiedpresentpresentnot present

Full-feed CSV columns

The full-feed CSV flattens attributes into one column per attribute name — but the column set is fixed and is not extended as new attribute types are added. Newer attributes (for example size-class, surface-type, stitched-edges, model-number, manufacturer-part-number, rgb-lighting) never appear in the CSV, even though they exist on those products in XML and JSON.

❗️

If attribute data matters to your integration, use the XML or JSON feed. The CSV will carry legacy attributes.

Within the columns that do exist, any given product only populates the attributes that apply to it. Map columns by header name, never by position. The header row as currently generated:

sku,name,price,rrp_incl,recommended_margin,promo_price,promo_starts,promo_ends,cptstock,jhbstock,dbnstock,nextshipmenteta,url,description,shortdesc,weight,length,width,height,"featured image","additional images","all images",categories,categoriesalt,3g-support,adjustable-pillow,amperage-a,android-version,aspect-ratio,audio-channels,battery-capacity,battery-type,battery-weight,bays,bluetooth,brand,buttons,cable-length-cm,cable-length-m,cable-type,cache,capacity,capacity-mah,channels,charging-port,charging-technology,class,colour,compatibility,connectivity,controller-included,cooler-included,cpu,cpu-cooler-height-mm,cpu-cores,cpu-series,cpu-socket,cpu-speed,curvature,dimm-slots,direction,disk-speed-rpm,dpi,ean,ecc,edging,enclosure,fan-size,fans,form-factor,format,fps,generation,hardness,hardware,has-adapter,hot-swappable,included-controller,input-interface,interface,ir,items,iteration,lighting,lock-type,material,maximum-resolution,memory-bus,memory-capacity,memory-clock-speed,memory-form-factor,memory-generation,memory-speed,microphone,module-type,motherboard-brand,motherboard-support,multiple-displays,noise-cancelling,number-of-fans,number-of-keys,number-of-lithium-ion-cells,output-interface,panel-type,pcie-862,platform,plug,poe,ports,power-delivery,power-delivery-w,power-va,profile,psu-energy-efficient,quickcharge,raincover-resistant,range-m,rank,read-speed,refresh-rate,remote-included,requirement,resolution,screen-size,series,side-panel,size,slots-size,socket,socket-support,software-series,software-version,speed,style,supported-cooler-height,supported-gpu-length-mm,surface,switch-brand,switch-cap,switch-colour,switch-height,switch-quantity,switch-series,thickness,tip,touch,usb-ports,voltage-v,warranty,water-resistant,watt-hours,wattage-w,weight-limit-kg,write-speed,currency

Full-feed JSON example (trimmed)

{
  "sku": "DM-12",
  "name": "Keychron Desk Mat - Pink",
  "price": 169,
  "rrp_incl": 299,
  "recommended_margin": 35,
  "promo_price": false,
  "promo_starts": false,
  "promo_ends": false,
  "cptstock": 0,
  "jhbstock": 0,
  "dbnstock": 0,
  "nextshipmenteta": "2026-10-02",
  "url": "https://www.syntech.co.za/product/keychron-desk-mat-pink/",
  "additional_images": ["...02.png", "...03.png"],
  "categories": "Computer peripherals, Mousepads, Coming Soon",
  "categorytree": "Computer peripherals > Mousepads|Coming Soon",
  "attributes": {
    "colour": "Pink",
    "surface-type": "Cloth (Control)",
    "brand": "Keychron",
    "warranty": "None"
  },
  "last_modified": "2026-08-18 03:25:40"
}

Attributes vary per product

Attribute names are kebab-case, and the set differs by product type — a keyboard exposes different attributes to a power station. Full feed only. Don't hardcode a fixed attribute list; discover them dynamically or map only the ones you need. brand, warranty, model-number and manufacturer-part-number are common across most products.

<attributes>
  <colour><![CDATA[ Pink ]]></colour>
  <size-class><![CDATA[ XXL / Desk ]]></size-class>
  <surface-type><![CDATA[ Cloth (Control) ]]></surface-type>
  <brand><![CDATA[ Keychron ]]></brand>
  <warranty><![CDATA[ None ]]></warranty>
</attributes>

Did this page help you?