Choicera Integration
Contract v1, as of 14 August 2026

Deliver product data

We only recommend products whose ingredients and dosage we know. For that we fetch your product data ourselves on a schedule. You never have to report changes to us.

What to do

  1. Send us a URL: a product feed, a sitemap or your shop domain. Which variant fits your shop system is listed under use an existing format. Done when the URL is publicly reachable over HTTPS, or you have given us a user and password for access.
  2. Make sure the required fields are present: article number, name, price as a number, availability, product description as HTML. Done when the self-test below prints nothing.
  3. Check that ingredients and nutrients are a real table in the description, not an image. Done when the table has a header row with an amount column, see description, nutrients, dosage.

JSON feed following our schema

The most precise path, and the one with the fewest follow-up questions. One HTTPS endpoint returns all products in a single document.

From youa feed URL over HTTPS
Product keyproduct_number
Cart keyvariants[].sku, pattern ^[A-Za-z0-9-]+$
Schemajson-feed.schema.json
Examplejson-feed.sample.json

Fields per product

FieldTypeRequiredMeaning
product_numberString, non-emptyyesArticle number, our key for the product. Must stay stable across feed generations.
titleString, non-emptyyesDisplay name.
health_claims.statusFixed values, see belowyesControls whether the product may appear in the advice.
variantsArray, min. 1yesEven a product without variants has exactly one entry.
body_htmlString with HTMLyesDescription as on the detail page, with ingredients, nutrient table and intake recommendation.
urlAbsolute HTTPS URLyesProduct detail page on the domain you told us.
imageHTTPS URLrecommendedOne cover image. The images list alone is not enough.
darreichungsformStringrecommendedDosage form: Kapseln, Tabletten, Pulver, Tropfen, Spray, Sachets, Set, Testkit.
images, tags, vendorArray, array, stringoptionalFurther images, categories and brand name.

Fields per variant

FieldTypeRequiredMeaning
skuString, only A-Z a-z 0-9 and hyphenyesUnique per variant. We send this value back in the cart link.
priceNumber, dot as decimal separator, grossyes24.9, not "24,90" and not "24.90".
availableBooleanyestrue or false, not "true" and not 1.
titleStringrecommendedSize label, for example 120 Kapseln or 500 g.
servings_per_containerNumberrecommendedServings per pack. Without this field we read the number from the product title, which fails for powders.
serving_size, net_quantity, gtinString, object, stringoptionalServing size, fill quantity and EAN. They make the price-per-serving comparison more precise.
idStringoptionalInternal ID of your shop, if you have one.

Values for health_claims.status

ValueMeaningConsequence
doneSupplement, health claims reviewedcan be recommended
keine_claimsSupplement without health claimscan be recommended
bundleSet of several productsin the catalogue, handled separately
kein_nemNot a supplement: test kits, accessories, cosmeticsstays out of the advice

Any other value, and a missing field, is treated like kein_nem. So set the status even when it is keine_claims. Test kits and accessories may stay in the feed, they are then excluded from the advice.

Running the feed

  • HTTPS and publicly reachable. Basic auth is possible, then send us user and password.
  • One document with all products, no pagination.
  • Response in under 20 seconds. For large catalogues serve a pre-generated file.
  • generated_at as an ISO 8601 timestamp so we can see how fresh the data is.
  • product_number and sku must not change on every generation.

Four rules that lose products

  1. No product_number or no title The product is discarded.
  2. No variant with a positive price The product is discarded. Deliver the price as a JSON number; as a safety net we also accept strings of the forms "3.95" and "3,95". Anything else (currency signs, thousands separators, 0) counts as no price. This is the most common reason a feed looks formally fine and zero products arrive on our side.
  3. Unknown health_claims.status The product sits in the catalogue but is invisible in the advice.
  4. Dot, underscore, space or slash in the sku The product can be recommended but drops out of the cart link, without an error message. The customer then sees an incomplete cart.

If url is not on the domain you told us, we discard the product link. Before the start that means: no test-shop domains in the feed.

Description, nutrients, dosage

Applies to every path. From the HTML description we read ingredients and dosage, the basis of every recommendation.

  • Real <table> markup with a header row and at least two columns. Tables as an image or as a div grid are unreadable for us.
  • First column: nutrient name. Sub-rows start with davon or a hyphen.
  • Amount column: the header cell must contain one of the words Tagesdosis, Menge or je. Recognised are for example pro Tagesdosis (3 Kapseln), je Portion (10 g) and Menge pro Kapsel. A je 100 g column is skipped because we need the per-serving basis.
  • NRV column, optional: header cell contains NRV or % RM.
  • Intake recommendation under a subheading <h3>Verzehrempfehlung</h3>, after the table.
<table>
  <tr><th>Nährstoff</th><th>pro Tagesdosis (3 Kapseln)</th><th>% NRV</th></tr>
  <tr><td>Magnesium</td><td>300 mg</td><td>80</td></tr>
  <tr><td>davon aus Magnesiumcitrat</td><td>1875 mg</td><td>-</td></tr>
</table>
<h3>Verzehrempfehlung</h3>
<p>3 Kapseln täglich mit reichlich Wasser zu einer Mahlzeit.</p>

The keywords above are German because the catalogues we read are German. If your shop publishes in another language, tell us and we add the vocabulary.

If you can emit ingredients and nutrients structured, so name, amount, unit and percent NRV per row, we prefer that over HTML. Then table parsing drops out.

Use an existing format

Four paths where your shop builds nothing new.

Shopify

We read the public products.json. From you we only need the shop domain. The cart runs over cart permalinks, the coupon over /discount/<code>.

WooCommerce, feed in Google Merchant format

If your shop already generates a feed for Google Merchant Center, we read that same feed. These are the fields we evaluate:

FieldRequiredNote
g:idyesProduct ID, digits only. The same value must work in the cart link.
g:titleyesDisplay name.
g:priceyesExactly amount currency, e.g. 24.90 EUR: dot as decimal separator, one space, ISO currency code. Any other format drops the product.
g:descriptionyesDescription as HTML, with nutrient table and intake recommendation.
g:linkyesProduct page on the live domain.
g:availabilityyesin_stock, out_of_stock, preorder or backorder.
g:image_linkrecommendedCover image, more via g:additional_image_link.
g:product_typerecommendedCategory path with >. The last level becomes our category and decides whether an item counts as a supplement.
g:sale_price, g:gtin, g:mpn, g:brandoptionalThe sale price is used when set. The rest improve product matching.

JSON-LD on product pages

No feed needed: we follow your sitemap and read the schema.org JSON-LD on each product page (Product or DietarySupplement) with sku, price and availability, plus the ingredient and nutrient table from the page HTML. Requirement: the JSON-LD is on every product page and carries the article number.

PrestaShop JSON

We read id_product, name, reference, price_current (falling back to price or price_default), description_short, description_webshop_full, images and link_rewrite for the product link.

Something else

Send us a sample of your export: a file, a URL or a list of the fields. We report whether we can read it directly or what we need.

Check it yourself

For the JSON feed. Every output line is a product that will not arrive, or will arrive incomplete.

npx --yes -p ajv-cli -p ajv-formats ajv validate --spec=draft2020 -c ajv-formats \
  -s json-feed.schema.json -d feed.json --errors=text --all-errors

pipx run check-jsonschema --schemafile json-feed.schema.json feed.json

Without installing anything, with jq only:

jq -r '.products[] | select((.product_number|type) != "string" or (.product_number|length) == 0
  or (.title|type) != "string" or (.title|length) == 0)
  | "DISCARDED (product_number/title): \(.title // "?")"' feed.json

jq -r '.products[] | select([.variants[]?.price | select(type == "number")] | length == 0)
  | "DISCARDED (no numeric price): \(.product_number)"' feed.json

jq -r '.products[] | .health_claims.status as $s
  | select((["done","keine_claims","bundle","kein_nem"] | index($s)) == null)
  | "INVISIBLE (health_claims.status): \(.product_number) = \($s|tostring)"' feed.json

jq -r '.products[] | .product_number as $p | .variants[]?
  | select((.sku // "") | test("^[A-Za-z0-9-]+$") | not)
  | "CART LOSS (sku): \($p) = \(.sku|tostring)"' feed.json

No output means the feed passes completely. Send us the URL and we check against it and report the product count and anything that stands out.

Next: step 2, hand over the cart.