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.
Get Started

Quickstart

Go from zero to a working Digital Product Passport in under 5 minutes.

This guide walks you through creating a product, attaching passport data, and retrieving it -- all with simple API calls.

Prerequisites

  • An EUlabel account with an API key (sk_test_... for sandbox, sk_live_... for production)
  • curl or any HTTP client

For the fastest start, use the sandbox first. You can migrate the same workflow to production by switching the base URL and API key.

Create a product

Every product needs a name, category, brand, and a valid GTIN (Global Trade Item Number).

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 do Crasto Douro Red 2021",
    "category": "wine",
    "brand": "Quinta do Crasto",
    "gtin": "5601234567890"
  }'
const EULABEL_API_KEY = process.env.EULABEL_API_KEY;

const response = 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 do Crasto Douro Red 2021',
    category: 'wine',
    brand: 'Quinta do Crasto',
    gtin: '5601234567890',
  }),
});

const product = await response.json();
console.log(product.productId);
import requests

EULABEL_API_KEY = "sk_test_..."

response = requests.post(
    "https://api.eulabel.eu/v1/products",
    headers={"Authorization": f"Bearer {EULABEL_API_KEY}"},
    json={
        "name": "Quinta do Crasto Douro Red 2021",
        "category": "wine",
        "brand": "Quinta do Crasto",
        "gtin": "5601234567890",
    },
)

product = response.json()
print(product["productId"])

Response:

{
  "productId": "a1b2c3d4-...",
  "qrCodeUrl": "https://eulabel.eu/01/05601234567890",
  "createdAt": "2026-03-14T20:00:00.000Z"
}

Save the productId -- you'll need it for the next steps.

Attach a Digital Product Passport

Attach wine e-label data (ingredients, nutrition, allergens, origin) to your product:

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": 2021,
      "ingredients": ["Grapes (Touriga Nacional, Touriga Franca)", "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
      },
      "origin": {
        "country": "PT",
        "region": "Douro",
        "designation": "Douro DOC"
      },
      "producers": [
        { "name": "Quinta do Crasto", "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: 2021,
      ingredients: ['Grapes (Touriga Nacional, Touriga Franca)', '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,
      },
      origin: { country: 'PT', region: 'Douro', designation: 'Douro DOC' },
      producers: [{ name: 'Quinta do Crasto', role: 'producer', country: 'PT' }],
    },
  }),
}).then(r => r.json());
response = requests.post(
    "https://api.eulabel.eu/v1/passports",
    headers={"Authorization": f"Bearer {EULABEL_API_KEY}"},
    json={
        "productId": "a1b2c3d4-...",
        "data": {
            "productType": "wine",
            "colour": "red",
            "vintage": 2021,
            "ingredients": ["Grapes (Touriga Nacional, Touriga Franca)", "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,
            },
            "origin": {"country": "PT", "region": "Douro", "designation": "Douro DOC"},
            "producers": [{"name": "Quinta do Crasto", "role": "producer", "country": "PT"}],
        },
    },
)

Response:

{
  "passportId": "e5f6g7h8-...",
  "productId": "a1b2c3d4-...",
  "version": 1,
  "status": "published",
  "createdAt": "2026-03-14T20:00:00.000Z"
}

Retrieve the passport

curl https://api.eulabel.eu/v1/products/a1b2c3d4-.../passport \
  -H "Authorization: Bearer $EULABEL_API_KEY"
const passport = await fetch(
  'https://api.eulabel.eu/v1/products/a1b2c3d4-.../passport',
  { headers: { 'Authorization': `Bearer ${EULABEL_API_KEY}` } }
).then(r => r.json());
passport = requests.get(
    "https://api.eulabel.eu/v1/products/a1b2c3d4-.../passport",
    headers={"Authorization": f"Bearer {EULABEL_API_KEY}"},
).json()

The response contains the full structured passport data for your product.

Download the QR code

curl https://api.eulabel.eu/v1/products/a1b2c3d4-.../qr \
  -H "Authorization: Bearer $EULABEL_API_KEY" \
  -o label-qr.svg
const svg = await fetch(
  'https://api.eulabel.eu/v1/products/a1b2c3d4-.../qr',
  { headers: { 'Authorization': `Bearer ${EULABEL_API_KEY}` } }
).then(r => r.text());

// Save to file or embed in your page
qr = requests.get(
    "https://api.eulabel.eu/v1/products/a1b2c3d4-.../qr",
    headers={"Authorization": f"Bearer {EULABEL_API_KEY}"},
)

with open("label-qr.svg", "wb") as f:
    f.write(qr.content)

This generates an SVG QR code encoding a GS1 Digital Link URI. When scanned, the resolver routes users to the appropriate passport view.

What happens when the QR code is scanned

Consumer scans QR code on wine bottle
         |
   eulabel.eu/01/05601234567890
         |
   Resolver detects audience context
         |
   Passport page displayed with ingredients, nutrition, allergens

The resolver serves different data to different audiences -- consumers see the product story, regulators get structured compliance data, and recyclers see material composition.

Next steps

On this page