# TypeScript SDK (https://eulabel.eu/docs/documentation/sdks/typescript)

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



At a glance [#at-a-glance]

* Install `@eulabel/sdk` and initialize with an **API key**.
* Use typed methods (`client.products.create`, `client.passports.create`) instead of raw HTTP.
* Handle failures via `EUlabelError` and build safe retries for transient errors.

SDK method reference [#sdk-method-reference]

| SDK method                                  | REST endpoint                    | Description                                 |
| ------------------------------------------- | -------------------------------- | ------------------------------------------- |
| `client.products.create(data)`              | `POST /v1/products`              | Create a product with a GS1 Digital Link    |
| `client.products.list()`                    | `GET /v1/products`               | List all products                           |
| `client.products.get(id)`                   | `GET /v1/products/{id}`          | Retrieve a single product                   |
| `client.products.update(id, data)`          | `PATCH /v1/products/{id}`        | Partial update on a product                 |
| `client.products.getPassport(id)`           | `GET /v1/products/{id}/passport` | Fetch DPP details for a product             |
| `client.products.getQrCode(id)`             | `GET /v1/products/{id}/qr`       | Download QR code SVG                        |
| `client.passports.create(data)`             | `POST /v1/passports`             | Create or update a Digital Product Passport |
| `client.analytics.getProduct(id, opts)`     | `GET /v1/analytics/product/{id}` | Retrieve scan analytics                     |
| `client.webhooks.verify(body, sig, secret)` | --                               | Verify inbound webhook HMAC signature       |

> **Warning**
> Do not use a live API key in client-side/browser code. Initialize the SDK on the server (API routes, workers, backend services).

Installation [#installation]

### Npm

```bash
npm install @eulabel/sdk
```
### Pnpm

```bash
pnpm add @eulabel/sdk
```
### Yarn

```bash
yarn add @eulabel/sdk
```
Initialization [#initialization]
```typescript
import { EUlabel } from '@eulabel/sdk';

const client = new EUlabel({
  apiKey: process.env.EULABEL_API_KEY,
});
```
Products [#products]

Create a product [#create-a-product]
```typescript
const product = await client.products.create({
  name: 'Quinta do Crasto Douro Red 2021',
  category: 'wine',
  brand: 'Quinta do Crasto',
  gtin: '5601234567890',
});

console.log(product.productId);
console.log(product.qrCodeUrl);
```
List products [#list-products]
```typescript
const { products } = await client.products.list();

for (const product of products) {
  console.log(product.name, product.gtin);
}
```
Get a product [#get-a-product]
```typescript
const product = await client.products.get('a1b2c3d4-...');
```
Passports [#passports]

Create a passport [#create-a-passport]
```typescript
const passport = await client.passports.create({
  productId: 'a1b2c3d4-...',
  data: {
    productType: 'wine',
    colour: 'red',
    vintage: 2021,
    ingredients: ['Grapes (Touriga Nacional)', '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' }],
  },
});
```
Fetch Digital Product Passport details [#fetch-digital-product-passport-details]

Retrieve the comprehensive DPP data (ingredients, nutrition, allergens, origin) for a specific product:
```typescript
const passport = await client.products.getPassport('a1b2c3d4-...');

console.log(passport.passportId);
console.log(passport.version);
console.log(passport.data.ingredients);
console.log(passport.data.nutrition.energyKcal);
console.log(passport.data.allergens.containsSulphites);
```
QR codes [#qr-codes]

Download a GS1 Digital Link QR code [#download-a-gs1-digital-link-qr-code]

Generate and download the SVG QR code for a product using its UUID:
```typescript
const svg = await client.products.getQrCode('a1b2c3d4-...');

// svg is a string containing the SVG markup
// Save to file or embed in your HTML
```
Analytics [#analytics]
```typescript
const analytics = await client.analytics.getProduct('a1b2c3d4-...', {
  start: '2026-01-21',
  end: '2026-04-21',
});

console.log(`Total scans: ${analytics.totalScans}`);
console.log(`Countries: ${analytics.uniqueCountries}`);
```
Error handling [#error-handling]
```typescript
import { EUlabel, EUlabelError } from '@eulabel/sdk';

try {
  await client.products.create({ name: 'Test', category: 'wine', brand: 'Test', gtin: 'invalid' });
} catch (error) {
  if (error instanceof EUlabelError) {
    console.error(error.type);    // "validation_error"
    console.error(error.message); // "Invalid GTIN check digit"
    console.error(error.param);   // "gtin"
  }
}
```
Webhook verification [#webhook-verification]
```typescript
import { EUlabel } from '@eulabel/sdk';

const isValid = client.webhooks.verify(
  requestBody,
  request.headers['x-eulabel-signature'],
  process.env.WEBHOOK_SECRET,
);
```
Response types [#response-types]

The SDK returns typed objects for all methods. Key interfaces:
```typescript
interface Product {
  productId: string;
  name: string;
  category: string;
  brand: string;
  gtin: string;
  qrCodeUrl: string;
  createdAt: string;
  updatedAt: string;
}

interface Passport {
  passportId: string;
  productId: string;
  version: number;
  status: 'published' | 'draft';
  data: PassportData;
  dataHash: string;
  createdAt: string;
}

interface AnalyticsResponse {
  productId: string;
  period: { start: string; end: string };
  totalScans: number;
  uniqueCountries: number;
  locations: Record<string, number>;
  devices: { mobile: number; desktop: number; tablet: number };
  topReferrers: Array<{ domain: string; scans: number }>;
  scansByDay: Array<{ date: string; scans: number }>;
}
```

### Common mistakes

* Passing a parsed JSON object instead of the raw request body for signature verification.
    * Retrying `validation_error` responses (fix the request instead).
    * Using production keys in non-production environments.


