Skip to main content

GS1 Digital Link Format

Optropic generates URLs following the GS1 Digital Link standard. This page explains the URL structure and how to parse them.

URL Structure

https://id.optropic.com/01/{GTIN}/21/{SERIAL}/10/{BATCH}?sig={SIGNATURE}

Path Components

SegmentAI CodeNameExample
01GTINGlobal Trade Item Number04260799580008
21SerialSerial NumberSN-2026-001
10BatchBatch/Lot NumberBATCH-A

Query Parameters

ParameterDescription
sigBase64url-encoded Ed25519 signature

Application Identifiers (AIs)

GS1 uses Application Identifiers to define data elements:

AINameFormatRequired
01GTIN14 digitsYes
21SerialUp to 20 alphanumericYes
10Batch/LotUp to 20 alphanumericNo
17Expiry DateYYMMDDNo

Complete Example

Generated URL

https://id.optropic.com/01/04260799580008/21/SN-2026-001/10/BATCH-A?sig=Kx7mN2vL9pQr5tYw8zAbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCDEFg

Parsed Components

ComponentValue
Domainid.optropic.com
GTIN (01)04260799580008
Serial (21)SN-2026-001
Batch (10)BATCH-A
SignatureKx7mN2vL9pQr5tYw8zAb...

Parsing the URL

JavaScript

function parseOptropicUrl(url) {
const parsed = new URL(url);
const pathParts = parsed.pathname.split('/').filter(Boolean);

const data = {};
for (let i = 0; i < pathParts.length; i += 2) {
const ai = pathParts[i];
const value = pathParts[i + 1];

switch (ai) {
case '01': data.gtin = value; break;
case '21': data.serial = value; break;
case '10': data.batch = value; break;
case '17': data.expiry = value; break;
}
}

data.signature = parsed.searchParams.get('sig');
return data;
}

// Usage
const code = parseOptropicUrl(
'https://id.optropic.com/01/04260799580008/21/SN-001/10/BATCH?sig=abc123'
);
console.log(code);
// { gtin: '04260799580008', serial: 'SN-001', batch: 'BATCH', signature: 'abc123' }

Python

from urllib.parse import urlparse, parse_qs

def parse_optropic_url(url: str) -> dict:
parsed = urlparse(url)
path_parts = [p for p in parsed.path.split('/') if p]

data = {}
for i in range(0, len(path_parts), 2):
ai = path_parts[i]
value = path_parts[i + 1] if i + 1 < len(path_parts) else None

if ai == '01':
data['gtin'] = value
elif ai == '21':
data['serial'] = value
elif ai == '10':
data['batch'] = value
elif ai == '17':
data['expiry'] = value

query = parse_qs(parsed.query)
data['signature'] = query.get('sig', [None])[0]

return data

Signature Verification

The sig parameter contains a Base64url-encoded Ed25519 signature over the path portion of the URL (excluding the domain and query string).

Signed Data

/01/04260799580008/21/SN-2026-001/10/BATCH-A

Verification Process

  1. Extract the path from the URL
  2. Decode the Base64url signature
  3. Verify using the registered Ed25519 public key
import { verify } from '@noble/ed25519';

async function verifySignature(url, publicKeyHex) {
const parsed = new URL(url);
const message = new TextEncoder().encode(parsed.pathname);

const signatureB64 = parsed.searchParams.get('sig');
const signature = base64urlDecode(signatureB64);

const publicKey = hexToBytes(publicKeyHex);

return await verify(signature, message, publicKey);
}

URL Encoding

Special characters in serial numbers or batch IDs must be URL-encoded:

CharacterEncoded
Space%20
/%2F
#%23
?%3F

Example

Serial SN/2026#001 becomes:

https://id.optropic.com/01/04260799580008/21/SN%2F2026%23001/10/BATCH?sig=...

QR Code Encoding

GS1 Digital Link URLs can be encoded in any QR code:

  • Error Correction: Level M (15%) recommended
  • Minimum Size: 25mm × 25mm at 300 DPI
  • Quiet Zone: 4 modules minimum

Best Practices

  1. Use alphanumeric mode when possible (shorter codes)
  2. Test scanning at various distances
  3. Include human-readable serial below the QR code
  4. Use high contrast (black on white)

Compression (Optional)

For very long URLs, GS1 Digital Link supports compression using the d query parameter. Optropic URLs are typically short enough that compression isn't needed.

Standards Compliance

Optropic GS1 Digital Links comply with:

  • GS1 Digital Link Standard 1.2
  • ISO/IEC 18004 (QR Code)
  • ISO/IEC 20248 (Digital Link standard)