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) curlor 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.svgconst 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 pageqr = 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, allergensThe resolver serves different data to different audiences -- consumers see the product story, regulators get structured compliance data, and recyclers see material composition.