Fetch the complete documentation index at: https://eulabel.eu/docs/llms.txt Use this file to discover all available pages before exploring further. Full content: https://eulabel.eu/docs/llms-full.txt Append .md to any page URL for markdown, or send Accept: text/markdown.

Wine E-Label Compliance

Create a fully compliant EU wine e-label with ingredients, nutrition, allergens, and origin data.

This guide walks you through creating a wine e-label that meets EU Regulation 2021/2117 requirements. By the end, your wine product will have a scannable QR code linking to a Digital Product Passport with all mandatory information.

EU Regulation 2021/2117 requires e-labels for wines produced and labelled after December 8, 2023. If you ship into multiple member states, your e-label must be available in the relevant languages.

At a glance

  • Outcome: a compliant e-label passport page + machine-readable dataset, reachable from a GS1 Digital Link QR code.
  • Time to first result: ~10 minutes in sandbox, then production.
  • What you’ll submit: ingredients, nutrition, allergens, origin, net content, alcohol by volume.

What's required

Full workflow

Register your product

Create a product entry with your wine's GTIN barcode:

export EULABEL_API_KEY="sk_test_..."

curl -X POST https://api.eulabel.eu/v1/products \
  -H "Authorization: Bearer $EULABEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Quinta dos Carvalhais Alfrocheiro 2019",
    "category": "wine",
    "brand": "Quinta dos Carvalhais",
    "gtin": "5601012012200"
  }'
const EULABEL_API_KEY = process.env.EULABEL_API_KEY;

const product = await fetch('https://api.eulabel.eu/v1/products', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${EULABEL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Quinta dos Carvalhais Alfrocheiro 2019',
    category: 'wine',
    brand: 'Quinta dos Carvalhais',
    gtin: '5601012012200',
  }),
}).then(r => r.json());

Save your productId

You’ll use the returned productId in the next steps:

PRODUCT_ID=a1b2c3d4-...

Attach the e-label data

Submit the full passport payload with ingredients, nutrition, allergens, and origin:

curl -X POST https://api.eulabel.eu/v1/passports \
  -H "Authorization: Bearer $EULABEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "a1b2c3d4-...",
    "data": {
      "productType": "wine",
      "colour": "red",
      "vintage": 2019,
      "alcoholByVolume": 14.0,
      "netContent": "750 mL",
      "ingredients": [
        "Grapes (Alfrocheiro)",
        "Sulphur Dioxide"
      ],
      "nutrition": {
        "energyKj": 351,
        "energyKcal": 84,
        "fatG": 0,
        "saturatedFatG": 0,
        "carbohydratesG": 0.6,
        "sugarsG": 0.3,
        "proteinG": 0.1,
        "saltG": 0,
        "alcoholG": 13.5
      },
      "allergens": {
        "containsSulphites": true,
        "containsEgg": false,
        "containsFish": false,
        "containsMilk": false,
        "containsCelery": false,
        "containsLupin": false
      },
      "origin": {
        "country": "PT",
        "region": "Dão",
        "designation": "Dão DOC"
      },
      "producers": [
        {
          "name": "Sogrape Vinhos S.A.",
          "role": "producer",
          "country": "PT"
        }
      ]
    }
  }'
const passport = await fetch('https://api.eulabel.eu/v1/passports', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${EULABEL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    productId: 'a1b2c3d4-...',
    data: {
      productType: 'wine',
      colour: 'red',
      vintage: 2019,
      alcoholByVolume: 14.0,
      netContent: '750 mL',
      ingredients: ['Grapes (Alfrocheiro)', 'Sulphur Dioxide'],
      nutrition: {
        energyKj: 351, energyKcal: 84,
        fatG: 0, saturatedFatG: 0,
        carbohydratesG: 0.6, sugarsG: 0.3,
        proteinG: 0.1, saltG: 0, alcoholG: 13.5,
      },
      allergens: {
        containsSulphites: true, containsEgg: false,
        containsFish: false, containsMilk: false,
        containsCelery: false, containsLupin: false,
      },
      origin: { country: 'PT', region: 'Dão', designation: 'Dão DOC' },
      producers: [{ name: 'Sogrape Vinhos S.A.', role: 'producer', country: 'PT' }],
    },
  }),
}).then(r => r.json());

Download the QR code

curl https://api.eulabel.eu/v1/products/a1b2c3d4-.../qr \
  -H "Authorization: Bearer $EULABEL_API_KEY" \
  -o wine-label-qr.svg

The QR code encodes a GS1 Digital Link URI (https://eulabel.eu/01/05601012012200). Print it on your wine label alongside the mandatory "e" symbol.

Verify the passport

Scan the QR code or visit the URL directly. The passport page displays:

  • Ingredients list with allergen highlights
  • Nutrition table per 100 mL
  • Origin and producer information
  • Regulatory compliance badges

Nutrition declaration format

All nutrition values are declared per 100 mL for beverages:

If you already store nutrition per serving, convert to per 100 mL before submitting. EUlabel validates required fields and returns actionable errors.

FieldTypeUnitRequired
energyKjnumberkJYes
energyKcalnumberkcalYes
fatGnumbergYes
saturatedFatGnumbergYes
carbohydratesGnumbergYes
sugarsGnumbergYes
proteinGnumbergYes
saltGnumbergYes
alcoholGnumberg (per 100 mL)Wine only

Allergen codes

FieldAllergenEU Regulation
containsSulphitesSulphur dioxide / sulphitesAnnex II, #12
containsEggEgg and egg productsAnnex II, #3
containsFishFish (isinglass fining)Annex II, #4
containsMilkMilk / casein (fining)Annex II, #7

FAQ

On this page